// ============================================================================
// /edit — admin UI pro Opatova DateShow
// ============================================================================
// Login (jen heslo) → admin panel se sekcemi:
//   - Show meta (episode, next show)
//   - Host (Opat) — single editable card
//   - Moderator — single editable card
//   - Current cast — 5 girls + 5 boys
//   - Best Energy — full list, add/edit/delete
//   - Archive episodes — add/edit/delete
//
// Používá window.dsAPI ze supabase-client.js
// ============================================================================

const { useState, useEffect, useRef, useCallback } = React;

// =====================================================================
// LOGIN
// =====================================================================
function LoginForm({ onLoggedIn }) {
  const [password, setPassword] = useState("");
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState("");

  async function submit(e) {
    e.preventDefault();
    if (loading) return;
    setLoading(true);
    setError("");
    try {
      const result = await window.dsAPI.login(password);
      if (result.ok) {
        onLoggedIn();
      } else {
        setError(result.error || "Špatné heslo");
      }
    } catch (err) {
      setError(err.message || String(err));
    } finally {
      setLoading(false);
    }
  }

  return (
    <div className="ed-login-wrap">
      <form className="ed-login-card" onSubmit={submit}>
        <div className="ed-login-heart">
          <svg viewBox="0 0 24 24" fill="currentColor" width="48" height="48">
            <path d="M12 21s-7.5-4.6-9.5-9.4C1 7.5 4 4 7.5 4c2 0 3.4 1 4.5 2.6C13.1 5 14.5 4 16.5 4 20 4 23 7.5 21.5 11.6 19.5 16.4 12 21 12 21z" />
          </svg>
        </div>
        <h1 className="ed-login-title">DateShow Admin</h1>
        <p className="ed-login-sub">Zadej heslo pro vstup do editace.</p>
        <input
          type="password"
          className="ed-input ed-login-input"
          placeholder="Heslo"
          autoFocus
          value={password}
          onChange={(e) => setPassword(e.target.value)}
          disabled={loading}
        />
        {error && <div className="ed-login-error">{error}</div>}
        <button type="submit" className="ed-btn ed-btn-primary ed-login-btn" disabled={loading || !password}>
          {loading ? "Přihlašuji…" : "Přihlásit"}
        </button>
      </form>
    </div>
  );
}

// =====================================================================
// PHOTO CROPPER — Discord-style modal s drag + zoom (vrací cropnutý File)
// =====================================================================
function PhotoCropper({ file, onConfirm, onCancel }) {
  const [imgSrc, setImgSrc] = useState("");
  const [zoom, setZoom] = useState(1);
  const [defaultZoom, setDefaultZoom] = useState(1);
  const [offset, setOffset] = useState({ x: 0, y: 0 });
  const [imgLoaded, setImgLoaded] = useState(false);
  const draggingRef = useRef(false);
  const lastPosRef = useRef(null);
  const imgRef = useRef(null);
  const containerRef = useRef(null);

  useEffect(() => {
    if (!file) return;
    const reader = new FileReader();
    reader.onload = (ev) => setImgSrc(ev.target.result);
    reader.readAsDataURL(file);
  }, [file]);

  function getPos(e) {
    if (e.touches && e.touches[0]) return { x: e.touches[0].clientX, y: e.touches[0].clientY };
    return { x: e.clientX, y: e.clientY };
  }
  function clampOffset(o, currentZoom) {
    const img = imgRef.current, container = containerRef.current;
    if (!img || !container || !img.naturalWidth) return o;
    const cropW = container.clientWidth, cropH = container.clientHeight;
    const baseScale = Math.max(cropW / img.naturalWidth, cropH / img.naturalHeight);
    const totalScale = baseScale * currentZoom;
    const scaledW = img.naturalWidth * totalScale, scaledH = img.naturalHeight * totalScale;
    const maxX = Math.abs(scaledW - cropW) / 2;
    const maxY = Math.abs(scaledH - cropH) / 2;
    return { x: Math.min(maxX, Math.max(-maxX, o.x)), y: Math.min(maxY, Math.max(-maxY, o.y)) };
  }
  function startDrag(e) { draggingRef.current = true; lastPosRef.current = getPos(e); }
  function moveDrag(e) {
    if (!draggingRef.current || !lastPosRef.current) return;
    e.preventDefault();
    const p = getPos(e);
    const dx = p.x - lastPosRef.current.x, dy = p.y - lastPosRef.current.y;
    lastPosRef.current = p;
    setOffset(o => clampOffset({ x: o.x + dx, y: o.y + dy }, zoom));
  }
  function endDrag() { draggingRef.current = false; lastPosRef.current = null; }
  function reset() { setZoom(defaultZoom); setOffset({ x: 0, y: 0 }); }
  useEffect(() => { setOffset(o => clampOffset(o, zoom)); }, [zoom]);
  function onImageLoad() {
    setImgLoaded(true);
    requestAnimationFrame(() => {
      const img = imgRef.current, container = containerRef.current;
      if (!img || !container || !img.naturalWidth) return;
      const cropW = container.clientWidth, cropH = container.clientHeight;
      const fit = Math.min(cropW / img.naturalWidth, cropH / img.naturalHeight);
      const cover = Math.max(cropW / img.naturalWidth, cropH / img.naturalHeight);
      const fitZoom = cover > 0 ? fit / cover : 1;
      setDefaultZoom(fitZoom);
      setZoom(fitZoom);
      setOffset({ x: 0, y: 0 });
    });
  }

  async function apply() {
    const img = imgRef.current, container = containerRef.current;
    if (!img || !container) return;
    const cropW = container.clientWidth, cropH = container.clientHeight;
    const naturalW = img.naturalWidth, naturalH = img.naturalHeight;
    const baseScale = Math.max(cropW / naturalW, cropH / naturalH);
    const totalScale = baseScale * zoom;
    const sourceW = cropW / totalScale, sourceH = cropH / totalScale;
    let sourceX = (naturalW - sourceW) / 2 - offset.x / totalScale;
    let sourceY = (naturalH - sourceH) / 2 - offset.y / totalScale;

    const OUT_W = Math.min(800, Math.max(200, Math.round(Math.min(sourceW, naturalW))));
    const OUT_H = Math.round(OUT_W * cropH / cropW);
    const canvas = document.createElement("canvas");
    canvas.width = OUT_W; canvas.height = OUT_H;
    const ctx = canvas.getContext("2d");
    ctx.fillStyle = "#000";
    ctx.fillRect(0, 0, OUT_W, OUT_H);
    const dstScaleX = OUT_W / sourceW, dstScaleY = OUT_H / sourceH;
    let sx = sourceX, sy = sourceY, sw = sourceW, sh = sourceH;
    let dx = 0, dy = 0, dw = OUT_W, dh = OUT_H;
    if (sx < 0) { dx = -sx * dstScaleX; dw += sx * dstScaleX; sw += sx; sx = 0; }
    if (sy < 0) { dy = -sy * dstScaleY; dh += sy * dstScaleY; sh += sy; sy = 0; }
    if (sx + sw > naturalW) { const cut = sx + sw - naturalW; sw -= cut; dw -= cut * dstScaleX; }
    if (sy + sh > naturalH) { const cut = sy + sh - naturalH; sh -= cut; dh -= cut * dstScaleY; }
    if (sw > 0 && sh > 0 && dw > 0 && dh > 0) ctx.drawImage(img, sx, sy, sw, sh, dx, dy, dw, dh);

    canvas.toBlob((blob) => {
      if (!blob) { onCancel(); return; }
      const cropped = new File([blob], `cropped-${Date.now()}.jpg`, { type: "image/jpeg" });
      onConfirm(cropped);
    }, "image/jpeg", 0.92);
  }

  return (
    <div className="ed-cropper-backdrop" onClick={onCancel}>
      <div className="ed-cropper" onClick={(e) => e.stopPropagation()}>
        <header className="ed-cropper-head">
          <h3>Upravit obrázek</h3>
          <button type="button" className="ed-cropper-x" onClick={onCancel}>✕</button>
        </header>
        <div ref={containerRef} className="ed-cropper-area"
          onMouseDown={startDrag} onMouseMove={moveDrag} onMouseUp={endDrag} onMouseLeave={endDrag}
          onTouchStart={startDrag} onTouchMove={moveDrag} onTouchEnd={endDrag}>
          {imgSrc && (
            <img ref={imgRef} src={imgSrc} alt="" draggable={false} onLoad={onImageLoad}
              style={{
                transform: `translate(calc(-50% + ${offset.x}px), calc(-50% + ${offset.y}px)) scale(${zoom})`,
                opacity: imgLoaded ? 1 : 0,
              }} />
          )}
          {!imgLoaded && <div className="ed-cropper-loading">Načítám…</div>}
        </div>
        <div className="ed-cropper-controls">
          <span className="ed-cropper-icon-min">🌄</span>
          <input type="range" min="0.4" max="4" step="0.02" value={zoom} onChange={(e) => setZoom(parseFloat(e.target.value))} />
          <span className="ed-cropper-icon-max">🖼️</span>
        </div>
        <footer className="ed-cropper-foot">
          <button type="button" className="ed-cropper-reset" onClick={reset}>Obnovit</button>
          <div className="ed-cropper-actions">
            <button type="button" className="ed-btn" onClick={onCancel}>Zrušit</button>
            <button type="button" className="ed-btn ed-btn-primary" onClick={apply}>Použít</button>
          </div>
        </footer>
      </div>
    </div>
  );
}

// =====================================================================
// PHOTO POSITION PICKER — sliders + preview pro object-position
// =====================================================================
function PhotoPositionPicker({ photoUrl, position, onChange, isNew = false }) {
  const parsed = (position || "50% 25%").replace(/center/g, "50%").split(/\s+/);
  const x = parseInt(parsed[0]) || 50;
  const y = parseInt(parsed[1]) || 25;

  function update(newX, newY) { onChange(`${newX}% ${newY}%`); }
  if (!photoUrl) return null;

  return (
    <div className={`ed-pos-picker ${isNew ? "is-new" : ""}`}>
      {isNew && <div className="ed-pos-new-banner">📷 Nová fotka — vycentruj před uložením</div>}
      <div className="ed-pos-preview">
        <img src={photoUrl} alt="" style={{ objectPosition: `${x}% ${y}%` }} />
        <div className="ed-pos-crosshair" style={{ left: `${x}%`, top: `${y}%` }}>+</div>
      </div>
      <div className="ed-pos-sliders">
        <label><span>Horizontálně: {x}%</span><input type="range" min="0" max="100" value={x} onChange={(e) => update(Number(e.target.value), y)} /></label>
        <label><span>Vertikálně: {y}%</span><input type="range" min="0" max="100" value={y} onChange={(e) => update(x, Number(e.target.value))} /></label>
        <div className="ed-pos-presets">
          {[
            { l: "Centrum", x: 50, y: 50 },
            { l: "Obličej", x: 50, y: 25 },
            { l: "Top", x: 50, y: 10 },
            { l: "Bottom", x: 50, y: 85 },
          ].map(p => <button key={p.l} type="button" className="ed-btn ed-btn-sm ed-btn-ghost" onClick={() => update(p.x, p.y)}>{p.l}</button>)}
        </div>
      </div>
    </div>
  );
}

// =====================================================================
// PHOTO UPLOAD
// =====================================================================
function PhotoUpload({ value, onChange, slug }) {
  const fileRef = useRef(null);
  const [uploading, setUploading] = useState(false);
  const [error, setError] = useState("");
  const [cropFile, setCropFile] = useState(null);  // soubor čekající na crop

  function handleFile(e) {
    const file = e.target.files && e.target.files[0];
    if (!file) return;
    setError("");
    setCropFile(file);  // → otevře cropper
  }

  async function uploadCropped(croppedFile) {
    setCropFile(null);
    setUploading(true);
    try {
      const { url } = await window.dsAPI.uploadPhoto(croppedFile, { slug });
      onChange(url);
    } catch (err) { setError(err.message || String(err)); }
    finally { setUploading(false); if (fileRef.current) fileRef.current.value = ""; }
  }

  function cancelCrop() {
    setCropFile(null);
    if (fileRef.current) fileRef.current.value = "";
  }

  return (
    <div className="ed-photo">
      {value ? (
        <img src={value} alt="" className="ed-photo-img" />
      ) : (
        <div className="ed-photo-placeholder">Bez fotky</div>
      )}
      <div className="ed-photo-actions">
        <button type="button" className="ed-btn ed-btn-sm" onClick={() => fileRef.current && fileRef.current.click()} disabled={uploading}>
          {uploading ? "Nahrávám…" : value ? "Změnit" : "Nahrát"}
        </button>
        {value && (
          <button type="button" className="ed-btn ed-btn-sm ed-btn-ghost" onClick={() => onChange("")} disabled={uploading}>
            Smazat
          </button>
        )}
        <input type="file" ref={fileRef} accept="image/*" onChange={handleFile} style={{ display: "none" }} />
      </div>
      {error && <div className="ed-error">{error}</div>}
      {value && <div className="ed-photo-url" title={value}>{value.length > 60 ? value.slice(0, 57) + "…" : value}</div>}
      {cropFile && (
        <PhotoCropper file={cropFile} onConfirm={uploadCropped} onCancel={cancelCrop} />
      )}
    </div>
  );
}

// =====================================================================
// FIELD HELPERS
// =====================================================================
function Field({ label, children, hint }) {
  return (
    <label className="ed-field">
      <span className="ed-field-label">{label}</span>
      {children}
      {hint && <span className="ed-field-hint">{hint}</span>}
    </label>
  );
}

function TextInput({ value, onChange, placeholder, type = "text" }) {
  return (
    <input
      type={type}
      className="ed-input"
      value={value == null ? "" : value}
      onChange={(e) => onChange(type === "number" ? (e.target.value === "" ? null : Number(e.target.value)) : e.target.value)}
      placeholder={placeholder}
    />
  );
}

function TextArea({ value, onChange, placeholder, rows = 3 }) {
  return (
    <textarea
      className="ed-input ed-textarea"
      value={value == null ? "" : value}
      onChange={(e) => onChange(e.target.value)}
      placeholder={placeholder}
      rows={rows}
    />
  );
}

function ArrayInput({ value, onChange, placeholder = "Nový bullet…" }) {
  const arr = Array.isArray(value) ? value : [];
  const [draft, setDraft] = useState("");

  function add() {
    const v = draft.trim();
    if (!v) return;
    onChange([...arr, v]);
    setDraft("");
  }
  function remove(i) {
    onChange(arr.filter((_, idx) => idx !== i));
  }
  function move(i, dir) {
    const next = [...arr];
    const j = i + dir;
    if (j < 0 || j >= next.length) return;
    [next[i], next[j]] = [next[j], next[i]];
    onChange(next);
  }
  function edit(i, val) {
    const next = [...arr];
    next[i] = val;
    onChange(next);
  }

  return (
    <div className="ed-array">
      {arr.map((item, i) => (
        <div key={i} className="ed-array-row">
          <input className="ed-input" value={item} onChange={(e) => edit(i, e.target.value)} />
          <button type="button" className="ed-btn ed-btn-icon" onClick={() => move(i, -1)} title="Nahoru">↑</button>
          <button type="button" className="ed-btn ed-btn-icon" onClick={() => move(i, 1)} title="Dolů">↓</button>
          <button type="button" className="ed-btn ed-btn-icon ed-btn-danger" onClick={() => remove(i)} title="Smazat">✕</button>
        </div>
      ))}
      <div className="ed-array-add">
        <input
          className="ed-input"
          value={draft}
          onChange={(e) => setDraft(e.target.value)}
          placeholder={placeholder}
          onKeyDown={(e) => { if (e.key === "Enter") { e.preventDefault(); add(); } }}
        />
        <button type="button" className="ed-btn ed-btn-sm" onClick={add}>+ Přidat</button>
      </div>
    </div>
  );
}

function Toggle({ value, onChange, label }) {
  return (
    <label className="ed-toggle">
      <input type="checkbox" checked={!!value} onChange={(e) => onChange(e.target.checked)} />
      <span>{label}</span>
    </label>
  );
}

// =====================================================================
// VOTES — reset (smaže všechny veřejné hlasy + vynuluje counter)
// =====================================================================
// VotesSection odstraněna na žádost — reset přes SQL: SELECT public.reset_all_votes();

// =====================================================================
// SHOW META FORM
// =====================================================================
function ShowMetaSection({ data, reload }) {
  const [draft, setDraft] = useState({ episode: data.episode, next_show: data.nextShow, show_name: data.showName });
  const [saving, setSaving] = useState(false);
  const [msg, setMsg] = useState("");

  async function save() {
    setSaving(true); setMsg("");
    try {
      await window.dsAPI.updateShowMeta({
        episode: draft.episode,
        next_show: draft.next_show,
        show_name: draft.show_name,
      });
      setMsg("Uloženo ✓");
      await reload();
    } catch (e) {
      setMsg("Chyba: " + e.message);
    } finally {
      setSaving(false);
    }
  }

  return (
    <Section title="📺 Epizoda" desc="Číslo aktuální epizody + datum dalšího vysílání.">
      <div className="ed-grid-2">
        <Field label="Aktuální epizoda (číslo)">
          <TextInput type="number" value={draft.episode} onChange={(v) => setDraft({ ...draft, episode: v })} />
        </Field>
        <Field label="Next show (string)" hint='např. "next-saturday-20:00"'>
          <TextInput value={draft.next_show} onChange={(v) => setDraft({ ...draft, next_show: v })} />
        </Field>
      </div>
      <div className="ed-actions">
        <button className="ed-btn ed-btn-primary" onClick={save} disabled={saving}>{saving ? "Ukládám…" : "Uložit"}</button>
        {msg && <span className="ed-msg">{msg}</span>}
      </div>
    </Section>
  );
}

// =====================================================================
// HOST FORM
// =====================================================================
function HostSection({ data, reload }) {
  const [draft, setDraft] = useState(data.host);
  const [saving, setSaving] = useState(false);
  const [msg, setMsg] = useState("");

  async function save() {
    setSaving(true); setMsg("");
    try {
      const { id, created_at, updated_at, ...rest } = draft;
      await window.dsAPI.updateHost(rest);
      setMsg("Uloženo ✓");
      await reload();
    } catch (e) { setMsg("Chyba: " + e.message); }
    finally { setSaving(false); }
  }

  return (
    <Section title="👑 Pořadatel (Opat)" desc="Single karta pořadatele — Opat04.">
      <div className="ed-form-row">
        <PhotoUpload value={draft.photo} onChange={(url) => setDraft({ ...draft, photo: url })} slug={`host-${draft.name || "opat"}`} />
        <div className="ed-form-fields">
          <div className="ed-grid-2">
            <Field label="Jméno"><TextInput value={draft.name} onChange={(v) => setDraft({ ...draft, name: v })} /></Field>
            <Field label="Role"><TextInput value={draft.role} onChange={(v) => setDraft({ ...draft, role: v })} /></Field>
            <Field label="Věk"><TextInput type="number" value={draft.age} onChange={(v) => setDraft({ ...draft, age: v })} /></Field>
            <Field label="Město"><TextInput value={draft.city} onChange={(v) => setDraft({ ...draft, city: v })} /></Field>
            <Field label="Instagram"><TextInput value={draft.instagram} onChange={(v) => setDraft({ ...draft, instagram: v })} /></Field>
          </div>
          <Field label="Bio"><TextArea value={draft.bio} onChange={(v) => setDraft({ ...draft, bio: v })} rows={4} /></Field>
        </div>
      </div>
      <div className="ed-actions">
        <button className="ed-btn ed-btn-primary" onClick={save} disabled={saving}>{saving ? "Ukládám…" : "Uložit"}</button>
        {msg && <span className="ed-msg">{msg}</span>}
      </div>
    </Section>
  );
}

// =====================================================================
// MODERATOR FORM
// =====================================================================
function ModeratorSection({ data, reload }) {
  const [draft, setDraft] = useState(data.moderator);
  const [saving, setSaving] = useState(false);
  const [msg, setMsg] = useState("");

  function setEpisodes(str) {
    const arr = str.split(",").map(s => s.trim()).filter(Boolean).map(Number).filter(n => !isNaN(n));
    setDraft({ ...draft, episodes: arr });
  }

  async function save() {
    setSaving(true); setMsg("");
    try {
      const { id, created_at, updated_at, ...rest } = draft;
      await window.dsAPI.updateModerator(rest);
      setMsg("Uloženo ✓");
      await reload();
    } catch (e) { setMsg("Chyba: " + e.message); }
    finally { setSaving(false); }
  }

  return (
    <Section title="🎤 Moderátor (rotující)" desc="Žolík tohoto týdne — mění se každou epizodu.">
      <div className="ed-form-row">
        <PhotoUpload value={draft.photo} onChange={(url) => setDraft({ ...draft, photo: url })} slug={`mod-${draft.name || "zolik"}`} />
        <div className="ed-form-fields">
          <div className="ed-grid-2">
            <Field label="Jméno"><TextInput value={draft.name} onChange={(v) => setDraft({ ...draft, name: v })} /></Field>
            <Field label="Role"><TextInput value={draft.role} onChange={(v) => setDraft({ ...draft, role: v })} /></Field>
            <Field label="Věk"><TextInput type="number" value={draft.age} onChange={(v) => setDraft({ ...draft, age: v })} /></Field>
            <Field label="Město"><TextInput value={draft.city} onChange={(v) => setDraft({ ...draft, city: v })} /></Field>
            <Field label="Instagram"><TextInput value={draft.instagram} onChange={(v) => setDraft({ ...draft, instagram: v })} /></Field>
            <Field label="Epizody (čísla, oddělené čárkou)" hint="např. 10, 14"><TextInput value={(draft.episodes || []).join(", ")} onChange={setEpisodes} /></Field>
          </div>
          <Field label="Note (krátký podpis nad kartou)"><TextInput value={draft.note} onChange={(v) => setDraft({ ...draft, note: v })} /></Field>
          <Field label="Bio"><TextArea value={draft.bio} onChange={(v) => setDraft({ ...draft, bio: v })} rows={4} /></Field>
        </div>
      </div>
      <div className="ed-actions">
        <button className="ed-btn ed-btn-primary" onClick={save} disabled={saving}>{saving ? "Ukládám…" : "Uložit"}</button>
        {msg && <span className="ed-msg">{msg}</span>}
      </div>
    </Section>
  );
}

// =====================================================================
// PARTICIPANT EDIT (collapsible row)
// =====================================================================
function ParticipantRow({ p, onSave, onDelete, onReplace, defaultOpen = false }) {
  const [open, setOpen] = useState(defaultOpen);
  const [draft, setDraft] = useState(p);
  const [saving, setSaving] = useState(false);
  const [msg, setMsg] = useState("");

  useEffect(() => { setDraft(p); }, [p]);

  function set(key, value) { setDraft({ ...draft, [key]: value }); }

  async function save() {
    setSaving(true); setMsg("");
    try {
      const { id, created_at, updated_at, ...rest } = draft;
      await onSave(p.id, rest);
      setMsg("✓");
      setTimeout(() => setMsg(""), 1500);
    } catch (e) { setMsg("Chyba: " + e.message); }
    finally { setSaving(false); }
  }

  async function del() {
    if (!confirm(`Opravdu smazat ${p.name} (${p.id})?`)) return;
    setSaving(true); setMsg("");
    try { await onDelete(p.id); }
    catch (e) { setMsg("Chyba: " + e.message); setSaving(false); }
  }

  return (
    <div className={`ed-row ${open ? "is-open" : ""}`}>
      <div className="ed-row-head" onClick={() => setOpen(!open)}>
        <div className="ed-row-thumb">
          {p.photo ? <img src={p.photo} alt="" /> : <div className="ed-row-thumb-empty">—</div>}
        </div>
        <div className="ed-row-meta">
          <div className="ed-row-name">
            {p.name}
            {p.tbd && <span className="ed-tag ed-tag-grey">TBD</span>}
            {p.unconfirmed && <span className="ed-tag ed-tag-purple">NEPOTVRZENO</span>}
            {p.winner && <span className="ed-tag ed-tag-pink">VÍTĚZ</span>}
          </div>
          <div className="ed-row-sub">
            {p.gender === "girl" ? "♀" : "♂"} {p.id}
            {p.age != null && ` · ${p.age}`}
            {p.city && ` · ${p.city}`}
            {p.appearances > 1 && ` · ${p.appearances}× v show`}
          </div>
        </div>
        <div className="ed-row-toggle">{open ? "−" : "+"}</div>
      </div>

      {open && (
        <div className="ed-row-body">
          <div className="ed-form-row">
            <PhotoUpload value={draft.photo} onChange={(url) => set("photo", url)} slug={p.id} />
            <div className="ed-form-fields">
              <div className="ed-grid-2">
                <Field label="Jméno"><TextInput value={draft.name} onChange={(v) => set("name", v)} /></Field>
                <Field label="Pohlaví">
                  <select className="ed-input" value={draft.gender} onChange={(e) => set("gender", e.target.value)}>
                    <option value="girl">♀ girl</option>
                    <option value="boy">♂ boy</option>
                  </select>
                </Field>
                <Field label="Real name"><TextInput value={draft.real_name} onChange={(v) => set("real_name", v)} /></Field>
                <Field label="Nick"><TextInput value={draft.nick} onChange={(v) => set("nick", v)} /></Field>
                <Field label="Alt name"><TextInput value={draft.alt_name} onChange={(v) => set("alt_name", v)} /></Field>
                <Field label="Věk"><TextInput type="number" value={draft.age} onChange={(v) => set("age", v)} /></Field>
                <Field label="Výška (cm)"><TextInput type="number" value={draft.height} onChange={(v) => set("height", v)} /></Field>
                <Field label="Město"><TextInput value={draft.city} onChange={(v) => set("city", v)} /></Field>
                <Field label="Instagram"><TextInput value={draft.instagram} onChange={(v) => set("instagram", v)} /></Field>
                <Field label="Job"><TextInput value={draft.job} onChange={(v) => set("job", v)} /></Field>
                <Field label="Epizoda (číslo)"><TextInput type="number" value={draft.episode} onChange={(v) => set("episode", v)} /></Field>
                <Field label="Počet účastí"><TextInput type="number" value={draft.appearances} onChange={(v) => set("appearances", v)} /></Field>
              </div>
              <Field label="Bio"><TextArea value={draft.bio} onChange={(v) => set("bio", v)} rows={3} /></Field>
              <Field label="Fact (krátká hláška)"><TextInput value={draft.fact} onChange={(v) => set("fact", v)} /></Field>
              <Field label="Info bullets (Best Energy)"><ArrayInput value={draft.info} onChange={(v) => set("info", v)} /></Field>

              <div className="ed-toggles">
                <Toggle label="V aktuálním castu (Tento týden)" value={draft.in_current_cast} onChange={(v) => set("in_current_cast", v)} />
                {draft.in_current_cast && (
                  <Field label="Pozice (1-5)">
                    <TextInput type="number" value={draft.cast_position} onChange={(v) => set("cast_position", v)} />
                  </Field>
                )}
                <Toggle label="V Best Energy" value={draft.in_best_energy} onChange={(v) => set("in_best_energy", v)} />
                {draft.in_best_energy && (
                  <Field label="Pozice v BE">
                    <TextInput type="number" value={draft.be_position} onChange={(v) => set("be_position", v)} />
                  </Field>
                )}
                <Toggle label="TBD (placeholder ?)" value={draft.tbd} onChange={(v) => set("tbd", v)} />
                <Toggle label="Nepotvrzená účast" value={draft.unconfirmed} onChange={(v) => set("unconfirmed", v)} />
                <Toggle label="Vítěz dílu" value={draft.winner} onChange={(v) => set("winner", v)} />
              </div>
            </div>
          </div>

          <div className="ed-actions">
            <button className="ed-btn ed-btn-primary" onClick={save} disabled={saving}>{saving ? "Ukládám…" : "Uložit"}</button>
            {onReplace && p.in_current_cast && (
              <button className="ed-btn" onClick={() => onReplace(p)} disabled={saving} title="Vyber existujícího účastníka který nahradí tento slot">
                ★ Nahradit z minulých
              </button>
            )}
            <button className="ed-btn ed-btn-danger ed-btn-ghost" onClick={del} disabled={saving}>Smazat</button>
            {msg && <span className="ed-msg">{msg}</span>}
          </div>
        </div>
      )}
    </div>
  );
}

// =====================================================================
// PARTICIPANTS LIST (filtered)
// =====================================================================
function ParticipantsList({ title, desc, items, onSave, onDelete, onReplace, actions, gridMode = false }) {
  return (
    <Section title={title} desc={desc}>
      <div className={`ed-list ${gridMode ? "ed-list-grid" : ""}`}>
        {items.map(p => (
          <ParticipantRow key={p.id} p={p} onSave={onSave} onDelete={onDelete} onReplace={onReplace} />
        ))}
      </div>
      {actions && <div className="ed-actions">{actions}</div>}
    </Section>
  );
}

// =====================================================================
// PARTICIPANT PICKER (modal — vyber z existujících)
// =====================================================================
function ParticipantPicker({ pool, onPick, onClose, title }) {
  const [query, setQuery] = useState("");
  const [genderFilter, setGenderFilter] = useState("all"); // all | girl | boy

  const filtered = pool.filter(p => {
    if (genderFilter !== "all" && p.gender !== genderFilter) return false;
    if (!query) return true;
    const q = query.toLowerCase();
    return (
      (p.name || "").toLowerCase().includes(q) ||
      (p.real_name || "").toLowerCase().includes(q) ||
      (p.nick || "").toLowerCase().includes(q) ||
      (p.id || "").toLowerCase().includes(q)
    );
  });

  return (
    <div className="ed-modal-backdrop" onClick={onClose}>
      <div className="ed-modal" onClick={(e) => e.stopPropagation()}>
        <header className="ed-modal-head">
          <h3>{title || "Vybrat z existujících"}</h3>
          <button className="ed-btn ed-btn-sm ed-btn-ghost" onClick={onClose}>✕ Zavřít</button>
        </header>
        <div className="ed-modal-toolbar">
          <input
            type="text"
            className="ed-input"
            placeholder="Hledat podle jména, přezdívky, real name…"
            value={query}
            onChange={(e) => setQuery(e.target.value)}
            autoFocus
          />
          <div className="ed-pick-tabs">
            {[
              { v: "all",  l: "Vše" },
              { v: "girl", l: "♀ Holky" },
              { v: "boy",  l: "♂ Kluci" },
            ].map(t => (
              <button
                key={t.v}
                type="button"
                className={`ed-btn ed-btn-sm ${genderFilter === t.v ? "ed-btn-primary" : "ed-btn-ghost"}`}
                onClick={() => setGenderFilter(t.v)}
              >{t.l}</button>
            ))}
          </div>
        </div>
        <div className="ed-modal-body">
          {filtered.length === 0 ? (
            <div className="ed-pick-empty">Nikdo nenalezen.</div>
          ) : (
            <div className="ed-pick-grid">
              {filtered.map(p => (
                <button
                  key={p.id}
                  type="button"
                  className="ed-pick-card"
                  onClick={() => onPick(p)}
                >
                  <div className="ed-pick-thumb">
                    {p.photo ? <img src={p.photo} alt="" /> : <span className="ed-row-thumb-empty">—</span>}
                  </div>
                  <div className="ed-pick-meta">
                    <div className="ed-pick-name">
                      <span className="ed-pick-gender">{p.gender === "girl" ? "♀" : "♂"}</span>
                      {p.name}
                      {p.appearances > 1 && <span className="ed-pick-app">{p.appearances}×</span>}
                    </div>
                    <div className="ed-pick-sub">
                      {p.nick && `“${p.nick}” · `}
                      {p.age != null && `${p.age} · `}
                      {p.city || p.id}
                    </div>
                  </div>
                </button>
              ))}
            </div>
          )}
        </div>
        <footer className="ed-modal-foot">
          <span className="ed-msg">{filtered.length} účastníků</span>
        </footer>
      </div>
    </div>
  );
}

// =====================================================================
// EPISODES SECTION
// =====================================================================
function EpisodeRow({ ep, onSave, onDelete }) {
  const [open, setOpen] = useState(false);
  const [draft, setDraft] = useState(ep);
  const [saving, setSaving] = useState(false);
  const [msg, setMsg] = useState("");

  useEffect(() => { setDraft(ep); }, [ep]);

  async function save() {
    setSaving(true); setMsg("");
    try {
      const { id, created_at, ...rest } = draft;
      await onSave(ep.id, rest);
      setMsg("✓");
      setTimeout(() => setMsg(""), 1500);
    } catch (e) { setMsg("Chyba: " + e.message); }
    finally { setSaving(false); }
  }

  async function del() {
    if (!confirm(`Smazat epizodu ${ep.episode_number}?`)) return;
    setSaving(true);
    try { await onDelete(ep.id); }
    catch (e) { setMsg("Chyba: " + e.message); setSaving(false); }
  }

  return (
    <div className={`ed-row ${open ? "is-open" : ""}`}>
      <div className="ed-row-head" onClick={() => setOpen(!open)}>
        <div className="ed-ep-num">EP {String(ep.episode_number).padStart(2, "0")}</div>
        <div className="ed-row-meta">
          <div className="ed-row-name">{ep.title}</div>
          <div className="ed-row-sub">
            {ep.air_date || "datum?"} · {ep.views ? ep.views.toLocaleString("cs-CZ") + " views" : "—"}
          </div>
        </div>
        <div className="ed-row-toggle">{open ? "−" : "+"}</div>
      </div>
      {open && (
        <div className="ed-row-body">
          <div className="ed-grid-2">
            <Field label="Číslo epizody"><TextInput type="number" value={draft.episode_number} onChange={(v) => setDraft({ ...draft, episode_number: v })} /></Field>
            <Field label="Datum (YYYY-MM-DD)"><TextInput value={draft.air_date} onChange={(v) => setDraft({ ...draft, air_date: v })} /></Field>
            <Field label="Title"><TextInput value={draft.title} onChange={(v) => setDraft({ ...draft, title: v })} /></Field>
            <Field label="Views"><TextInput type="number" value={draft.views} onChange={(v) => setDraft({ ...draft, views: v })} /></Field>
            <Field label="Link (YT/Discord)"><TextInput value={draft.link} onChange={(v) => setDraft({ ...draft, link: v })} /></Field>
          </div>
          <div className="ed-actions">
            <button className="ed-btn ed-btn-primary" onClick={save} disabled={saving}>{saving ? "Ukládám…" : "Uložit"}</button>
            <button className="ed-btn ed-btn-danger ed-btn-ghost" onClick={del} disabled={saving}>Smazat</button>
            {msg && <span className="ed-msg">{msg}</span>}
          </div>
        </div>
      )}
    </div>
  );
}

// =====================================================================
// STORIES — Příběhy z DateShow
// =====================================================================
function StoryRow({ s, onSave, onDelete }) {
  const [open, setOpen] = useState(false);
  const [draft, setDraft] = useState(s);
  const [saving, setSaving] = useState(false);
  const [msg, setMsg] = useState("");

  useEffect(() => { setDraft(s); }, [s]);

  function set(key, val) { setDraft({ ...draft, [key]: val }); }

  async function save() {
    setSaving(true); setMsg("");
    try {
      const { id, created_at, updated_at, ...rest } = draft;
      await onSave(s.id, rest);
      setMsg("✓");
      setTimeout(() => setMsg(""), 1500);
    } catch (e) { setMsg("Chyba: " + e.message); }
    finally { setSaving(false); }
  }

  async function del() {
    if (!confirm(`Smazat příběh „${s.title}"?`)) return;
    setSaving(true);
    try { await onDelete(s.id); }
    catch (e) { setMsg("Chyba: " + e.message); setSaving(false); }
  }

  return (
    <div className={`ed-row ${open ? "is-open" : ""}`}>
      <div className="ed-row-head" onClick={() => setOpen(!open)}>
        <div className="ed-row-thumb">
          {s.image ? <img src={s.image} alt="" /> : <div className="ed-row-thumb-empty">—</div>}
        </div>
        <div className="ed-row-meta">
          <div className="ed-row-name" dangerouslySetInnerHTML={{ __html: s.title || "(bez názvu)" }} />
          <div className="ed-row-sub">
            {s.date_text || "—"} {s.episode != null && `· EP ${s.episode}`} {s.is_visible === false && "· skrytý"}
          </div>
        </div>
        <div className="ed-row-toggle">{open ? "−" : "+"}</div>
      </div>
      {open && (
        <div className="ed-row-body">
          <div className="ed-form-row">
            <PhotoUpload value={draft.image} onChange={(url) => set("image", url)} slug={`story-${draft.title || "novy"}`} />
            <div className="ed-form-fields">
              <div className="ed-grid-2">
                <Field label="Datum (volný text)" hint='např. "27.03.2026" nebo "Minulý týden"'>
                  <TextInput value={draft.date_text} onChange={(v) => set("date_text", v)} />
                </Field>
                <Field label="Epizoda (číslo)"><TextInput type="number" value={draft.episode} onChange={(v) => set("episode", v)} /></Field>
                <Field label="Pozice (ordering)"><TextInput type="number" value={draft.position} onChange={(v) => set("position", v)} /></Field>
                <Field label="Image fit" hint="cover / contain / anchor-bottom">
                  <select className="ed-input" value={draft.image_fit || "cover"} onChange={(e) => set("image_fit", e.target.value)}>
                    <option value="cover">cover (vyplní celé)</option>
                    <option value="contain">contain (sedne dovnitř)</option>
                    <option value="anchor-bottom">anchor-bottom (kotví dole)</option>
                  </select>
                </Field>
              </div>
              <Field label="Title" hint="HTML povoleno: <em>, <strong>"><TextInput value={draft.title} onChange={(v) => set("title", v)} /></Field>
              <Field label="Description" hint="HTML povoleno: <em>, <strong>"><TextArea value={draft.description} onChange={(v) => set("description", v)} rows={4} /></Field>
              <Toggle label="Viditelný na webu" value={draft.is_visible !== false} onChange={(v) => set("is_visible", v)} />
            </div>
          </div>
          <div className="ed-actions">
            <button className="ed-btn ed-btn-primary" onClick={save} disabled={saving}>{saving ? "Ukládám…" : "Uložit"}</button>
            <button className="ed-btn ed-btn-danger ed-btn-ghost" onClick={del} disabled={saving}>Smazat</button>
            {msg && <span className="ed-msg">{msg}</span>}
          </div>
        </div>
      )}
    </div>
  );
}

function StoriesSection({ stories, reload }) {
  async function add() {
    const nextPos = (stories.reduce((m, s) => Math.max(m, s.position || 0), 0)) + 1;
    try {
      await window.dsAPI.createStory({
        title: "Nový příběh",
        date_text: "",
        position: nextPos,
        image_fit: "cover",
        is_visible: true,
      });
      await reload();
    } catch (e) { alert("Chyba: " + e.message); }
  }
  async function save(id, fields) { await window.dsAPI.updateStory(id, fields); await reload(); }
  async function del(id) { await window.dsAPI.deleteStory(id); await reload(); }

  return (
    <Section title="📖 Příběhy z DateShow" desc="Sekce 'Příběhy' na hlavní stránce — kompletně editovatelná.">
      <div className="ed-list">
        {(stories || []).map(s => <StoryRow key={s.id} s={s} onSave={save} onDelete={del} />)}
      </div>
      <div className="ed-actions">
        <button className="ed-btn ed-btn-primary" onClick={add}>+ Přidat příběh</button>
      </div>
    </Section>
  );
}

// =====================================================================
// PHOTO REVIEW MODAL — full-size náhled cropnuté + originální fotky,
// volitelně re-crop přes PhotoCropper (admin upraví špatně cropnutou)
// =====================================================================
function PhotoReviewModal({ photoUrl, photoOriginalUrl, onClose, onRecrop, busy }) {
  const [cropFile, setCropFile] = useState(null);
  const [loadingOriginal, setLoadingOriginal] = useState(false);

  async function startRecrop() {
    if (!photoOriginalUrl) return;
    setLoadingOriginal(true);
    try {
      const res = await fetch(photoOriginalUrl);
      const blob = await res.blob();
      const ext = blob.type === "image/png" ? "png" : "jpg";
      const f = new File([blob], `original.${ext}`, { type: blob.type });
      setCropFile(f);
    } catch (e) {
      alert("Chyba načítání originálu: " + e.message);
    } finally {
      setLoadingOriginal(false);
    }
  }

  if (cropFile) {
    return <PhotoCropper file={cropFile} onConfirm={(croppedFile) => { setCropFile(null); onRecrop(croppedFile); }} onCancel={() => setCropFile(null)} />;
  }

  return (
    <div className="ed-cropper-backdrop" onClick={onClose}>
      <div className="ed-cropper" onClick={(e) => e.stopPropagation()} style={{ maxWidth: 720 }}>
        <header className="ed-cropper-head">
          <h3>Náhled fotky</h3>
          <button type="button" className="ed-cropper-x" onClick={onClose}>✕</button>
        </header>
        <div style={{ padding: 16, display: "grid", gridTemplateColumns: photoOriginalUrl ? "1fr 1fr" : "1fr", gap: 12, maxHeight: "60vh", overflow: "auto" }}>
          <div style={{ display: "flex", flexDirection: "column", gap: 6, alignItems: "center" }}>
            <div style={{ fontSize: 11, opacity: 0.6, textTransform: "uppercase", letterSpacing: "0.06em" }}>Cropnutá (zobrazí se uživatelům)</div>
            <img src={photoUrl} alt="cropnutá" style={{ maxWidth: "100%", maxHeight: "55vh", borderRadius: 8, objectFit: "contain" }} />
          </div>
          {photoOriginalUrl && (
            <div style={{ display: "flex", flexDirection: "column", gap: 6, alignItems: "center" }}>
              <div style={{ fontSize: 11, opacity: 0.6, textTransform: "uppercase", letterSpacing: "0.06em" }}>Originál (full size)</div>
              <img src={photoOriginalUrl} alt="originál" style={{ maxWidth: "100%", maxHeight: "55vh", borderRadius: 8, objectFit: "contain" }} />
            </div>
          )}
        </div>
        <footer className="ed-cropper-foot">
          <a href={photoOriginalUrl || photoUrl} target="_blank" rel="noreferrer noopener" className="ed-cropper-reset">
            ↗ Otevřít v novém tabu
          </a>
          <div className="ed-cropper-actions">
            <button type="button" className="ed-btn" onClick={onClose} disabled={busy}>Zavřít</button>
            {photoOriginalUrl && (
              <button type="button" className="ed-btn ed-btn-primary" onClick={startRecrop} disabled={loadingOriginal || busy}>
                {loadingOriginal ? "Načítám…" : busy ? "Ukládám…" : "🔍 Re-cropnout"}
              </button>
            )}
            {!photoOriginalUrl && (
              <span className="ed-msg" style={{ fontSize: 11, opacity: 0.6 }}>Originál není uložen — starý příspěvek</span>
            )}
          </div>
        </footer>
      </div>
    </div>
  );
}

// =====================================================================
// CONTRIBUTIONS — fronta příspěvků z /contribute (anyone uploaded)
// =====================================================================
function ContributionRow({ c, allParticipants, onApply, onReject, onDelete, onUpdate }) {
  const [open, setOpen] = useState(false);
  const [draft, setDraft] = useState(c);
  const [busy, setBusy] = useState(false);
  const [msg, setMsg] = useState("");
  const [reviewing, setReviewing] = useState(null);  // { photo, original } pro modal

  useEffect(() => { setDraft(c); }, [c]);

  function set(key, val) { setDraft({ ...draft, [key]: val }); }

  // Re-crop: upload nové cropnuté fotky, update suggested_changes.photo
  async function handleRecrop(croppedFile) {
    setBusy(true); setMsg("");
    try {
      const up = await window.dsAPI.uploadContributionPhoto(croppedFile);
      const newChanges = { ...(c.suggested_changes || {}), photo: up.url };
      await onUpdate(c.id, { suggested_changes: newChanges });
      setReviewing(null);
      setMsg("Fotka re-cropnuta ✓");
      setTimeout(() => setMsg(""), 2000);
    } catch (e) {
      alert("Chyba při ukládání re-cropu: " + e.message);
    } finally { setBusy(false); }
  }

  // Najdi participant info podle ID
  const matchedParticipant = c.participant_id
    ? allParticipants.find(p => p.id === c.participant_id)
    : null;

  async function saveEdits() {
    setBusy(true); setMsg("");
    try {
      const { id, created_at, ...rest } = draft;
      await onUpdate(c.id, rest);
      setMsg("Uloženo ✓");
      setTimeout(() => setMsg(""), 1500);
    } catch (e) { setMsg("Chyba: " + e.message); }
    finally { setBusy(false); }
  }

  async function apply() {
    if (!confirm(`Aplikovat příspěvek na ${matchedParticipant ? matchedParticipant.name : "(NEZNÁMÝ ÚČASTNÍK)"}?\n\nFotka, IG handle a popis se zkopírují do účastníkovy karty. Jednotlivá pole můžeš ručně doupravit potom.`)) return;
    setBusy(true); setMsg("");
    try {
      await onApply(c.id);
      setMsg("Aplikováno ✓");
    } catch (e) { setMsg("Chyba: " + e.message); }
    finally { setBusy(false); }
  }

  async function reject() {
    if (!confirm("Označit jako odmítnutý? Nebude se aplikovat.")) return;
    setBusy(true);
    try { await onReject(c.id); }
    catch (e) { setMsg("Chyba: " + e.message); setBusy(false); }
  }

  async function del() {
    if (!confirm("Definitivně smazat příspěvek + nahranou fotku?")) return;
    setBusy(true);
    try { await onDelete(c.id); }
    catch (e) { setMsg("Chyba: " + e.message); setBusy(false); }
  }

  const statusColor = c.status === "applied" ? "ed-tag-pink" : c.status === "rejected" ? "ed-tag-grey" : "ed-tag-purple";
  const statusLabel = c.status === "applied" ? "APLIKOVÁNO" : c.status === "rejected" ? "ODMÍTNUTÝ" : "PENDING";

  return (
    <div className={`ed-row ${open ? "is-open" : ""}`}>
      <div className="ed-row-head" onClick={() => setOpen(!open)}>
        <div className="ed-row-thumb">
          {c.photo_url ? <img src={c.photo_url} alt="" /> : <div className="ed-row-thumb-empty">—</div>}
        </div>
        <div className="ed-row-meta">
          <div className="ed-row-name">
            {matchedParticipant ? matchedParticipant.name : (c.participant_name_typed || "(neznámý účastník)")}
            <span className={`ed-tag ${statusColor}`}>{statusLabel}</span>
          </div>
          <div className="ed-row-sub">
            od <strong>{c.contributor_name}</strong> · {c.contributor_contact}
            {c.ig_handle && ` · IG: ${c.ig_handle}`}
            {" · "}{new Date(c.created_at).toLocaleString("cs-CZ", { dateStyle: "short", timeStyle: "short" })}
          </div>
        </div>
        <div className="ed-row-toggle">{open ? "−" : "+"}</div>
      </div>
      {open && (
        <div className="ed-row-body">
          <div className="ed-form-row">
            <div className="ed-photo">
              {c.photo_url ? (
                <a href={c.photo_url} target="_blank" rel="noreferrer noopener">
                  <img src={c.photo_url} alt="" className="ed-photo-img" />
                </a>
              ) : (
                <div className="ed-photo-placeholder">Bez fotky</div>
              )}
            </div>
            <div className="ed-form-fields">
              <div className="ed-grid-2">
                <Field label="Účastník (linknutý)">
                  <select className="ed-input" value={draft.participant_id || ""} onChange={(e) => set("participant_id", e.target.value || null)}>
                    <option value="">— Nelinknutý —</option>
                    {allParticipants.map(p => (
                      <option key={p.id} value={p.id}>
                        {p.gender === "girl" ? "♀" : "♂"} {p.name} ({p.id})
                      </option>
                    ))}
                  </select>
                </Field>
                <Field label="IG handle (návrh od kontributora)"><TextInput value={draft.ig_handle} onChange={(v) => set("ig_handle", v)} /></Field>
                <Field label="Účastník — typed (pokud nenašel v listu)"><TextInput value={draft.participant_name_typed} onChange={(v) => set("participant_name_typed", v)} /></Field>
                <Field label="Status">
                  <select className="ed-input" value={draft.status} onChange={(e) => set("status", e.target.value)}>
                    <option value="pending">pending</option>
                    <option value="applied">applied</option>
                    <option value="rejected">rejected</option>
                  </select>
                </Field>
              </div>
              <Field label="Popis / info od kontributora"><TextArea value={draft.description_note} onChange={(v) => set("description_note", v)} rows={4} /></Field>

              {/* JSONB navržené změny — z /uprav formu */}
              {c.suggested_changes && Object.keys(c.suggested_changes).length > 0 && (
                <div className="ed-changes-block">
                  <div className="ed-changes-head">📝 Navržené změny ({Object.keys(c.suggested_changes).length} polí)</div>
                  <table className="ed-changes-table">
                    <thead><tr><th>Pole</th><th>Současné</th><th>Navržené</th></tr></thead>
                    <tbody>
                      {Object.entries(c.suggested_changes).filter(([k]) => k !== "photo_original").map(([k, v]) => {
                        const current = matchedParticipant ? matchedParticipant[k] : null;
                        const display = (val) => Array.isArray(val) ? val.join(", ") : (val == null ? "—" : String(val));
                        const isPhoto = k === "photo";
                        const originalUrl = c.suggested_changes.photo_original;
                        return (
                          <tr key={k}>
                            <td><code>{k}</code></td>
                            <td className="ed-changes-current">
                              {isPhoto && current ? <img src={current} alt="" style={{ maxWidth: 60, borderRadius: 6 }} /> : display(current)}
                            </td>
                            <td className="ed-changes-new">
                              {isPhoto && v ? (
                                <div style={{ display: "flex", flexDirection: "column", gap: 4, alignItems: "flex-start" }}>
                                  <img src={v} alt="" style={{ maxWidth: 80, borderRadius: 6, cursor: "pointer" }} onClick={() => setReviewing({ photo: v, original: originalUrl })} />
                                  <button type="button" className="ed-btn ed-btn-sm ed-btn-ghost" onClick={() => setReviewing({ photo: v, original: originalUrl })}>
                                    🔍 {originalUrl ? "Náhled / re-crop" : "Náhled"}
                                  </button>
                                </div>
                              ) : <strong>{display(v)}</strong>}
                            </td>
                          </tr>
                        );
                      })}
                    </tbody>
                  </table>
                </div>
              )}

              <Field label="Admin poznámky (interní)"><TextArea value={draft.admin_notes} onChange={(v) => set("admin_notes", v)} rows={2} /></Field>
            </div>
          </div>

          <div className="ed-actions">
            {c.status === "pending" && (
              <>
                <button className="ed-btn ed-btn-primary" onClick={apply} disabled={busy || !draft.participant_id}>
                  ✓ Aplikovat
                </button>
                <button className="ed-btn" onClick={reject} disabled={busy}>Odmítnout</button>
              </>
            )}
            <button className="ed-btn" onClick={saveEdits} disabled={busy}>Uložit změny</button>
            <button className="ed-btn ed-btn-danger ed-btn-ghost" onClick={del} disabled={busy}>Smazat</button>
            {msg && <span className="ed-msg">{msg}</span>}
          </div>
        </div>
      )}
      {reviewing && (
        <PhotoReviewModal
          photoUrl={reviewing.photo}
          photoOriginalUrl={reviewing.original}
          busy={busy}
          onClose={() => setReviewing(null)}
          onRecrop={handleRecrop}
        />
      )}
    </div>
  );
}

function ContributionsSection({ data, reload }) {
  const [contribs, setContribs] = useState([]);
  const [filter, setFilter] = useState("pending"); // pending | all
  const [loading, setLoading] = useState(false);

  const allParticipants = [...(data.girls || []), ...(data.boys || []), ...(data.allParticipants || [])]
    .filter((p, i, arr) => arr.findIndex(x => x.id === p.id) === i)
    .sort((a, b) => (a.name || "").localeCompare(b.name || ""));

  async function loadList() {
    setLoading(true);
    try {
      const list = await window.dsAPI.listContributions({ pendingOnly: filter === "pending" });
      setContribs(list);
    } catch (e) { alert("Chyba: " + e.message); }
    finally { setLoading(false); }
  }

  useEffect(() => { loadList(); }, [filter]);

  async function apply(id) { await window.dsAPI.applyContribution(id); await loadList(); await reload(); }
  async function reject(id) { await window.dsAPI.updateContribution(id, { status: "rejected", rejected_at: new Date().toISOString() }); await loadList(); }
  async function del(id) { await window.dsAPI.deleteContribution(id); await loadList(); }
  async function update(id, fields) { await window.dsAPI.updateContribution(id, fields); await loadList(); }

  const pendingCount = contribs.filter(c => c.status === "pending").length;

  return (
    <Section
      title={`📥 Příspěvky od fanoušků${pendingCount > 0 ? ` (${pendingCount} nových)` : ""}`}
      desc='Lidé z webu mohou přes /contribute nahrávat fotky, IG handles a info. Tady je projdeš a klikem "Aplikovat" zkopíruješ do účastníkovy karty.'
    >
      <div className="ed-actions" style={{ marginBottom: 8 }}>
        <button className={`ed-btn ed-btn-sm ${filter === "pending" ? "ed-btn-primary" : ""}`} onClick={() => setFilter("pending")}>Pending ({pendingCount})</button>
        <button className={`ed-btn ed-btn-sm ${filter === "all" ? "ed-btn-primary" : ""}`} onClick={() => setFilter("all")}>Všechny</button>
        <button className="ed-btn ed-btn-sm ed-btn-ghost" onClick={loadList} disabled={loading}>{loading ? "Načítám…" : "↻ Refresh"}</button>
      </div>

      {contribs.length === 0 ? (
        <div className="ed-msg" style={{ padding: "20px 0" }}>
          {filter === "pending" ? "Žádné nové příspěvky." : "Žádné příspěvky."}
        </div>
      ) : (
        <div className="ed-list">
          {contribs.map(c => (
            <ContributionRow
              key={c.id}
              c={c}
              allParticipants={allParticipants}
              onApply={apply}
              onReject={reject}
              onDelete={del}
              onUpdate={update}
            />
          ))}
        </div>
      )}
    </Section>
  );
}

function EpisodesSection({ episodes, reload }) {
  async function add() {
    const next = (episodes[0] && episodes[0].episode_number) ? episodes[0].episode_number + 1 : 1;
    try {
      await window.dsAPI.createEpisode({ episode_number: next, title: "Nová epizoda", views: 0 });
      await reload();
    } catch (e) { alert("Chyba: " + e.message); }
  }
  async function save(id, fields) { await window.dsAPI.updateEpisode(id, fields); await reload(); }
  async function del(id) { await window.dsAPI.deleteEpisode(id); await reload(); }

  return (
    <Section title="📼 Archiv epizod" desc="Předchozí epizody — title, datum, views.">
      <div className="ed-list">
        {episodes.map(ep => <EpisodeRow key={ep.id} ep={ep} onSave={save} onDelete={del} />)}
      </div>
      <div className="ed-actions">
        <button className="ed-btn ed-btn-primary" onClick={add}>+ Přidat epizodu</button>
      </div>
    </Section>
  );
}

// =====================================================================
// SECTION WRAPPER
// =====================================================================
function Section({ title, desc, children }) {
  const [open, setOpen] = useState(true);
  return (
    <section className={`ed-section ${open ? "is-open" : "is-collapsed"}`}>
      <header className="ed-section-head" onClick={() => setOpen(!open)}>
        <div>
          <h2 className="ed-section-title">{title}</h2>
          {desc && <p className="ed-section-desc">{desc}</p>}
        </div>
        <span className="ed-section-toggle">{open ? "−" : "+"}</span>
      </header>
      {open && <div className="ed-section-body">{children}</div>}
    </section>
  );
}

// =====================================================================
// ADMIN PANEL — root after login
// =====================================================================
function AdminPanel({ data, reload, onLogout }) {
  // pickerMode: null | "add" | { mode: "replace", target: <participant> }
  const [pickerMode, setPickerMode] = useState(null);
  const [resettingVotes, setResettingVotes] = useState(false);

  async function saveParticipant(id, fields) { await window.dsAPI.updateParticipant(id, fields); await reload(); }
  async function deleteParticipant(id) { await window.dsAPI.deleteParticipant(id); await reload(); }

  async function resetVotes() {
    const totalVotes = (data.allParticipants || []).reduce((s, p) => s + (p.votes || 0), 0)
                     + ((data.girls || []).concat(data.boys || [])).reduce((s, p) => s + (p.votes || 0), 0);
    if (!confirm(`SMAZAT VŠECHNY HLASY?\n\nTeď je ${totalVotes} hlasů. Po klikem to bude 0 a všichni vóteři budou moct znovu hlasovat.\n\nNevratné.`)) return;
    setResettingVotes(true);
    try {
      const deleted = await window.dsAPI.resetAllVotes();
      alert(`✓ Smazáno ${deleted} hlasů.`);
      await reload();
    } catch (e) { alert("Chyba: " + e.message); }
    finally { setResettingVotes(false); }
  }

  // Najde nejnižší volný slot 1-5 v aktuálním castu pro daný gender;
  // pokud jsou všechny obsazené, vrátí max+1 (přetečení).
  function nextCastPosition(gender) {
    const list = gender === "girl" ? data.girls : data.boys;
    const taken = new Set(list.filter(p => !p.tbd).map(p => p.cast_position));
    for (let i = 1; i <= 5; i++) if (!taken.has(i)) return i;
    return list.reduce((m, p) => Math.max(m, p.cast_position || 0), 0) + 1;
  }

  // Nový (prázdný) účastník do castu
  async function addCastMember(gender) {
    const id = `${gender === "girl" ? "g" : "b"}-${Date.now().toString(36)}`;
    const nextPos = nextCastPosition(gender);
    try {
      await window.dsAPI.createParticipant({
        id, gender, name: "Nový " + (gender === "girl" ? "♀" : "♂"),
        in_current_cast: true, cast_position: nextPos,
        in_best_energy: false, appearances: 1, tbd: false,
      });
      await reload();
    } catch (e) { alert("Chyba: " + e.message); }
  }

  // Vybere existujícího (z BE / minulých dílů) a přesune do castu.
  // Pokud je pickerMode { mode:"replace", target }, smaže původní TBD/slot
  // a vybranému dá pozici po něm.
  // VŽDY nastaví in_best_energy = false, protože v BE se zobrazují jen ti
  // kdo už NENÍ v aktuálním castu (jinak by se ten člověk objevil dvakrát na webu).
  async function pickIntoCast(p) {
    const replaceTarget = pickerMode && pickerMode.mode === "replace" ? pickerMode.target : null;
    const targetPos = replaceTarget ? replaceTarget.cast_position : nextCastPosition(p.gender);
    try {
      if (replaceTarget && replaceTarget.id !== p.id) {
        await window.dsAPI.deleteParticipant(replaceTarget.id);
      }
      await window.dsAPI.updateParticipant(p.id, {
        in_current_cast: true,
        in_best_energy: false,            // ← důležité: vyřadit z BE pokud tam byl
        cast_position: targetPos,
        appearances: (p.appearances || 0) + 1,
        episode: data.episode,
        tbd: false,
      });
      setPickerMode(null);
      await reload();
    } catch (e) { alert("Chyba: " + e.message); }
  }

  // Pool pro picker — všichni co nejsou už v current castu
  const pickerPool = (data.allParticipants || []).filter(p => !p.in_current_cast);

  async function addBestEnergy() {
    const id = `ae-${Date.now().toString(36)}`;
    const nextPos = (data.allParticipants.reduce((m, p) => Math.max(m, p.be_position || 0), 0)) + 1;
    try {
      await window.dsAPI.createParticipant({
        id, gender: "boy", name: "Nový účastník",
        in_current_cast: false, in_best_energy: true,
        be_position: nextPos, appearances: 1, episode: data.episode,
      });
      await reload();
    } catch (e) { alert("Chyba: " + e.message); }
  }

  return (
    <div className="ed-app">
      <header className="ed-topbar">
        <div className="ed-topbar-brand">
          <svg viewBox="0 0 24 24" fill="currentColor" width="22" height="22"><path d="M12 21s-7.5-4.6-9.5-9.4C1 7.5 4 4 7.5 4c2 0 3.4 1 4.5 2.6C13.1 5 14.5 4 16.5 4 20 4 23 7.5 21.5 11.6 19.5 16.4 12 21 12 21z" /></svg>
          <span>DateShow Admin</span>
          <span className="ed-topbar-ep">EP {data.episode}</span>
        </div>
        <div className="ed-topbar-actions">
          <button className="ed-btn ed-btn-sm ed-btn-danger ed-btn-ghost" onClick={resetVotes} disabled={resettingVotes} title="Smazat všechny hlasy v BE">
            {resettingVotes ? "Mažu…" : "🗑️ Reset hlasy"}
          </button>
          <a href="/" className="ed-btn ed-btn-sm ed-btn-ghost">↗ Web</a>
          <button className="ed-btn ed-btn-sm ed-btn-ghost" onClick={onLogout}>Odhlásit</button>
        </div>
      </header>

      <main className="ed-main">
        <ShowMetaSection data={data} reload={reload} />
        <HostSection data={data} reload={reload} />
        <ModeratorSection data={data} reload={reload} />

        <ParticipantsList
          title="👯 Aktuální cast (Tento týden)"
          desc="5 holek + 5 kluků pro nadcházející díl. Můžeš vybrat z minulých účastníků nebo přidat nového."
          items={[...data.girls, ...data.boys]}
          onSave={saveParticipant}
          onDelete={deleteParticipant}
          onReplace={(target) => setPickerMode({ mode: "replace", target })}
          actions={
            <>
              <button className="ed-btn ed-btn-primary" onClick={() => setPickerMode("add")}>
                ★ Vybrat z minulých
              </button>
              <button className="ed-btn" onClick={() => addCastMember("girl")}>+ Nová ♀</button>
              <button className="ed-btn" onClick={() => addCastMember("boy")}>+ Nový ♂</button>
            </>
          }
        />

        <ParticipantsList
          title="🔥 Best Energy"
          desc="Všichni účastníci napříč epizodami. Klikni na kartu pro úpravy."
          items={data.allParticipants}
          onSave={saveParticipant}
          onDelete={deleteParticipant}
          gridMode={true}
          actions={
            <button className="ed-btn ed-btn-primary" onClick={addBestEnergy}>+ Přidat do Best Energy</button>
          }
        />

        <ContributionsSection data={data} reload={reload} />

        <StoriesSection stories={data.stories} reload={reload} />

        <EpisodesSection episodes={data.episodes} reload={reload} />
      </main>

      {pickerMode && (
        <ParticipantPicker
          title={
            pickerMode.mode === "replace"
              ? `Nahradit slot „${pickerMode.target.name}" (pozice ${pickerMode.target.cast_position}) vybraným účastníkem`
              : "Vybrat z minulých účastníků (do aktuálního castu)"
          }
          pool={
            pickerMode.mode === "replace"
              ? pickerPool.filter(p => p.gender === pickerMode.target.gender)
              : pickerPool
          }
          onPick={pickIntoCast}
          onClose={() => setPickerMode(null)}
        />
      )}
    </div>
  );
}

// =====================================================================
// ROOT — handles auth state
// =====================================================================
function EditApp() {
  const [loggedIn, setLoggedIn] = useState(null); // null = checking
  const [data, setData] = useState(null);
  const [error, setError] = useState("");

  useEffect(() => {
    if (!window.dsAPI) {
      setError("dsAPI not available — Supabase client failed to load.");
      return;
    }
    window.dsAPI.isLoggedIn().then(setLoggedIn).catch(() => setLoggedIn(false));
    const sub = window.dsAPI.onAuthChange((logged) => setLoggedIn(logged));
    return () => { if (sub && sub.data && sub.data.subscription) sub.data.subscription.unsubscribe(); };
  }, []);

  const reload = useCallback(async () => {
    try {
      const d = await window.dsAPI.loadAllData();
      setData(d);
    } catch (e) { setError(e.message); }
  }, []);

  useEffect(() => { if (loggedIn) reload(); }, [loggedIn, reload]);

  async function logout() {
    await window.dsAPI.logout();
    setLoggedIn(false);
    setData(null);
  }

  if (error) return <div className="ed-fatal">⚠️ {error}</div>;
  if (loggedIn === null) return <div className="ed-loading">Načítám…</div>;
  if (!loggedIn) return <LoginForm onLoggedIn={() => setLoggedIn(true)} />;
  if (!data) return <div className="ed-loading">Načítám data…</div>;
  return <AdminPanel data={data} reload={reload} onLogout={logout} />;
}

window.EditApp = EditApp;
