// Playlists — fully interactive
const { useState: plUseState, useMemo: plUseMemo, useEffect: plUseEffect, useRef: plUseRef } = React;
const { PLAYLISTS: PL_SEED, MEDIA_FILES: PL_MEDIA } = window.DATA;

const PL_COLORS = ["#F5A623", "#60A5FA", "#A78BFA", "#34D399", "#F87171", "#22D3EE", "#FB923C", "#EC4899"];

const PL_LAYOUTS = [
  { id: "full", label: "Plein écran", grid: "1fr / 1fr", zones: ["A"] },
  { id: "split-h", label: "2 colonnes", grid: "1fr / 1fr 1fr", zones: ["A", "B"] },
  { id: "split-v", label: "2 lignes", grid: "1fr 1fr / 1fr", zones: ["A", "B"] },
  { id: "main-side", label: "Principal + 2", grid: "1fr 1fr / 2fr 1fr", zones: ["A", "B", "C"], spans: { A: "1 / 1 / 3 / 2" } },
  { id: "quad", label: "Quadrants 2×2", grid: "1fr 1fr / 1fr 1fr", zones: ["A", "B", "C", "D"] },
  { id: "main3", label: "Mosaïque", grid: "2fr 1fr 1fr / 2fr 1fr", zones: ["A", "B", "C", "D"], spans: { A: "1 / 1 / 3 / 2", B: "1 / 2 / 2 / 3", C: "2 / 2 / 3 / 3", D: "3 / 1 / 4 / 3" } },
];
window.PL_LAYOUT_ZONES = Object.fromEntries(PL_LAYOUTS.map(l => [l.id, l.zones]));

const ZONE_COLORS = { A: "#F5A623", B: "#60A5FA", C: "#A78BFA", D: "#34D399" };

// Build a seeded initial playlist with example items per zone
function makeSeedZones() {
  return {
    A: [
      { id: "i1", mediaId: "m10", title: "Présentation projet ZAC Lormont", type: "pdf", duration: "45 s" },
      { id: "i2", mediaId: "m1", title: "Rapport Q3 2026 — Chantiers", type: "pdf", duration: "30 s" },
      { id: "i3", mediaId: "m4", title: "Chantier Bayonne — Drone 4K", type: "vid", duration: "2 min 34" },
      { id: "i4", mediaId: "m2", title: "All-Hands Mars 2026", type: "ppt", duration: "12 min" },
    ],
    B: [
      { id: "i5", mediaId: "m6", title: "Sécurité — Jours sans accident", type: "kpi", duration: "Auto" },
      { id: "i6", mediaId: "m3", title: "Indicateurs sécurité Avril", type: "xls", duration: "20 s" },
    ],
    C: [
      { id: "i7", mediaId: "m5", title: "Équipe TP — Bayonne", type: "img", duration: "15 s" },
      { id: "i8", mediaId: "m9", title: "Visite chantier Anglet", type: "yt", duration: "3 min 12" },
    ],
    D: [
      { id: "i9", mediaId: "m7", title: "LinkedIn @elsia — Flux", type: "soc", duration: "10 s / post" },
      { id: "i10", mediaId: "m11", title: "Météo Bordeaux", type: "met", duration: "Auto" },
      { id: "i11", mediaId: "m12", title: "Anniversaires de mai", type: "txt", duration: "20 s" },
    ],
  };
}

const PL_SEED_WITH_ZONES = PL_SEED.map((p, idx) => ({
  ...p,
  layout: "quad",
  zones: idx === 0 ? makeSeedZones() : {
    A: PL_MEDIA.slice(0, Math.min(p.items, 6)).map((m, i) => ({
      id: `${p.id}-i${i}`, mediaId: m.id, title: m.name, type: m.type, duration: m.duration
    })),
    B: [], C: [], D: [],
  },
}));

let nextItemId = 1000;
const newItemId = () => `i${++nextItemId}`;

// ===== Helpers ==================================================
function ZonePreview2({ layout, selectedZone, setSelectedZone, zones }) {
  const lay = PL_LAYOUTS.find(l => l.id === layout) || PL_LAYOUTS[4];
  return (
    <div className="zone-preview" style={{ grid: lay.grid }}>
      {lay.zones.map((z) => (
        <button
          key={z}
          className={"zone-preview__zone" + (selectedZone === z ? " is-active" : "")}
          style={{
            gridArea: lay.spans?.[z] || "auto",
            color: ZONE_COLORS[z],
          }}
          onClick={() => setSelectedZone(z)}
        >
          <div className="zone-preview__letter">{z}</div>
          <div className="zone-preview__count">{(zones?.[z] || []).length} tuile{(zones?.[z] || []).length > 1 ? "s" : ""}</div>
        </button>
      ))}
    </div>
  );
}

// ===== Modal infrastructure =====================================
function Modal({ open, onClose, title, children, footer, size }) {
  if (!open) return null;
  return (
    <div className="modal-backdrop" onClick={onClose}>
      <div className={"modal" + (size === "lg" ? " modal--lg" : "")} onClick={e => e.stopPropagation()}>
        <div className="modal__header">
          <div className="modal__title">{title}</div>
          <button className="btn btn--sm btn--icon btn--ghost" onClick={onClose}><Icon.X size={14} /></button>
        </div>
        <div className="modal__body">{children}</div>
        {footer && <div className="modal__footer">{footer}</div>}
      </div>
    </div>
  );
}

// ===== New / Edit Playlist Form =================================
function PlaylistForm({ initial, onSubmit, onCancel, submitLabel }) {
  const [name, setName] = plUseState(initial?.name || "");
  const [schedule, setSchedule] = plUseState(initial?.schedule || "");
  const [layout, setLayout] = plUseState(initial?.layout || "quad");
  const [color, setColor] = plUseState(initial?.color || PL_COLORS[0]);

  const submit = () => {
    if (!name.trim()) return;
    onSubmit({ name: name.trim(), schedule: schedule.trim() || "Non planifié", layout, color });
  };

  return (
    <>
      <div className="form-field">
        <label>Nom de la playlist</label>
        <input
          className="form-input"
          placeholder="Ex. Boucle matinée — Accueil"
          value={name}
          onChange={e => setName(e.target.value)}
          autoFocus
        />
      </div>
      <div className="form-field">
        <label>Disposition du mur</label>
        <div className="layout-picker__grid">
          {PL_LAYOUTS.map(l => (
            <button
              key={l.id}
              type="button"
              className={"layout-picker__item" + (layout === l.id ? " is-active" : "")}
              onClick={() => setLayout(l.id)}
              title={l.label}
            >
              <div className="layout-picker__mini" style={{ grid: l.grid }}>
                {l.zones.map(z => (
                  <div key={z} style={{ gridArea: l.spans?.[z] || "auto" }} />
                ))}
              </div>
              <span className="layout-picker__name">{l.label}</span>
            </button>
          ))}
        </div>
      </div>
      <div className="form-field">
        <label>Planification</label>
        <input
          className="form-input"
          placeholder="Ex. Lun–Ven · 07:00 – 12:00"
          value={schedule}
          onChange={e => setSchedule(e.target.value)}
        />
        <div className="form-help">Texte libre — peut être édité plus tard depuis le Planning.</div>
      </div>
      <div className="form-field">
        <label>Couleur d'identification</label>
        <div className="color-picker">
          {PL_COLORS.map(c => (
            <button
              key={c}
              type="button"
              className={"color-swatch" + (color === c ? " is-active" : "")}
              style={{ background: c }}
              onClick={() => setColor(c)}
            />
          ))}
        </div>
      </div>
      <div className="modal__actions">
        <button className="btn" onClick={onCancel}>Annuler</button>
        <button className="btn btn--primary" onClick={submit} disabled={!name.trim()}>
          {submitLabel || "Créer la playlist"}
        </button>
      </div>
    </>
  );
}

// ===== Media Picker =============================================
function MediaPicker({ onPick, onClose, excludeIds = [], mediaFiles }) {
  const list = mediaFiles || PL_MEDIA;
  const [search, setSearch] = plUseState("");
  const [filter, setFilter] = plUseState("all");
  const types = [
    { id: "all", label: "Tous" },
    { id: "pdf", label: "PDF" },
    { id: "ppt", label: "Présentations" },
    { id: "xls", label: "Tableaux" },
    { id: "vid", label: "Vidéos" },
    { id: "img", label: "Photos" },
    { id: "kpi", label: "KPI" },
    { id: "soc", label: "Réseaux" },
    { id: "txt", label: "Annonces" },
    { id: "yt", label: "YouTube" },
    { id: "met", label: "Météo" },
  ];
  const files = list.filter(f =>
    (filter === "all" || f.type === filter) &&
    (!search || f.name.toLowerCase().includes(search.toLowerCase()))
  );
  return (
    <div className="media-picker">
      <div className="media-picker__head">
        <div className="search" style={{ flex: 1 }}>
          <Icon.Search />
          <input
            placeholder="Rechercher un média dans la bibliothèque…"
            value={search}
            onChange={e => setSearch(e.target.value)}
            autoFocus
          />
        </div>
      </div>
      <div className="media-picker__chips">
        {types.map(t => (
          <button
            key={t.id}
            className={"chip" + (filter === t.id ? " chip--active" : "")}
            onClick={() => setFilter(t.id)}
          >{t.label}</button>
        ))}
      </div>
      <div className="media-picker__grid">
        {files.length === 0 && (
          <div className="media-picker__empty">Aucun média ne correspond.</div>
        )}
        {files.map(f => (
          <button
            key={f.id}
            className="media-picker__item"
            onClick={() => onPick(f)}
          >
            <div className="media-picker__thumb">
              <MediaThumb type={f.type} name={f.name} />
            </div>
            <div className="media-picker__info">
              <div className="media-picker__name">{f.name}</div>
              <div className="media-picker__meta">
                <TypeTag type={f.type} />
                <span className="dim">· {f.duration}</span>
              </div>
            </div>
            <Icon.Plus size={14} />
          </button>
        ))}
      </div>
    </div>
  );
}

// ===== Confirm dialog ===========================================
function ConfirmDialog({ open, title, message, danger, onConfirm, onCancel }) {
  if (!open) return null;
  return (
    <div className="modal-backdrop" onClick={onCancel}>
      <div className="modal modal--sm" onClick={e => e.stopPropagation()}>
        <div className="modal__header">
          <div className="modal__title">{title}</div>
        </div>
        <div className="modal__body" style={{ fontSize: 13, color: "var(--text-secondary)" }}>{message}</div>
        <div className="modal__actions" style={{ padding: "12px 16px 16px" }}>
          <button className="btn" onClick={onCancel}>Annuler</button>
          <button className={"btn " + (danger ? "btn--danger" : "btn--primary")} onClick={onConfirm}>
            {danger ? "Supprimer" : "Confirmer"}
          </button>
        </div>
      </div>
    </div>
  );
}

// ===== Toast ====================================================
function Toast({ msg }) {
  if (!msg) return null;
  return <div className="toast">{msg.icon}{msg.text}</div>;
}

// ===== MAIN =====================================================
function Playlists({ playlists, setPlaylists, mediaFiles, pushActivity }) {
  const [selectedId, setSelectedId] = plUseState(playlists[0]?.id);
  const [zone, setZone] = plUseState("A");
  const [search, setSearch] = plUseState("");

  // Modals
  const [showNew, setShowNew] = plUseState(false);
  const [showEdit, setShowEdit] = plUseState(false);
  const [pickerZone, setPickerZone] = plUseState(null); // when set, opens media picker for this zone
  const [confirmDel, setConfirmDel] = plUseState(null); // playlist id pending deletion
  const [editingName, setEditingName] = plUseState(false);
  const [toast, setToast] = plUseState(null);

  const flashToast = (text, icon) => {
    setToast({ text, icon });
    setTimeout(() => setToast(null), 2400);
  };

  // If the selected playlist no longer exists (deleted from another view), pick first available
  plUseEffect(() => {
    if (selectedId && !playlists.find(p => p.id === selectedId)) {
      setSelectedId(playlists[0]?.id);
    }
  }, [playlists, selectedId]);

  const playlist = playlists.find(p => p.id === selectedId);
  const lay = PL_LAYOUTS.find(l => l.id === playlist?.layout) || PL_LAYOUTS[4];

  // When layout changes & current zone not valid, reset
  plUseEffect(() => {
    if (playlist && !lay.zones.includes(zone)) setZone(lay.zones[0]);
  }, [playlist?.layout]);

  const filteredPlaylists = playlists.filter(p =>
    !search || p.name.toLowerCase().includes(search.toLowerCase())
  );

  // ----- mutations ----------
  const totalItems = (p) => Object.values(p.zones || {}).reduce((s, arr) => s + (arr?.length || 0), 0);

  const createPlaylist = (data) => {
    const tempId = "p" + Date.now();
    const newPl = {
      id: tempId, name: data.name, schedule: data.schedule, color: data.color,
      layout: data.layout, active: false, lastUsed: "Jamais utilisée",
      items: 0, duration: "—",
      zones: { A: [], B: [], C: [], D: [] },
    };
    setPlaylists(prev => [newPl, ...prev]);
    setSelectedId(tempId);
    setShowNew(false);
    pushActivity?.({ action: "a créé la playlist", target: data.name, type: "publish" });
    flashToast(`Playlist « ${data.name} » créée`, <Icon.Check size={13} />);
    // Persistance backend
    if (window.ELSIA_API) {
      window.ELSIA_API.createPlaylist({ name: data.name, schedule: data.schedule, color: data.color, layout: data.layout })
        .then(persisted => {
          // Remplacer l'id temporaire par l'id renvoyé par l'API
          setPlaylists(prev => prev.map(p => p.id === tempId ? { ...p, id: persisted.id } : p));
          setSelectedId(persisted.id);
        })
        .catch(err => console.error("[api] createPlaylist échec:", err));
    }
  };

  const updatePlaylist = (id, patch) => {
    setPlaylists(prev => prev.map(p => p.id === id ? { ...p, ...patch } : p));
    // Persistance backend si patch contient des champs serveur (name, color, schedule, layout)
    if (window.ELSIA_API && id.startsWith("p_")) {
      const serverPatch = {};
      for (const k of ["name", "color", "schedule", "layout"]) {
        if (patch[k] !== undefined) serverPatch[k] = patch[k];
      }
      if (Object.keys(serverPatch).length > 0) {
        window.ELSIA_API.updatePlaylist(id, serverPatch).catch(err => console.error("[api] updatePlaylist:", err));
      }
    }
  };

  // Persiste les zones (items) d'une playlist en backend
  const persistZones = (playlistId, zones) => {
    if (!window.ELSIA_API || !playlistId || !playlistId.startsWith("p_")) return;
    const payload = {};
    for (const [z, items] of Object.entries(zones || {})) {
      payload[z] = (items || []).map(it => ({
        media_id: it.mediaId,
        custom_duration_sec: it.custom_duration_sec || null,
      }));
    }
    window.ELSIA_API.setPlaylistItems(playlistId, payload)
      .catch(err => console.error("[api] setPlaylistItems:", err));
  };

  const deletePlaylist = (id) => {
    const name = playlists.find(p => p.id === id)?.name;
    setPlaylists(prev => prev.filter(p => p.id !== id));
    if (selectedId === id) {
      const rest = playlists.filter(p => p.id !== id);
      setSelectedId(rest[0]?.id);
    }
    setConfirmDel(null);
    pushActivity?.({ action: "a supprimé la playlist", target: name, type: "alert" });
    flashToast(`Playlist « ${name} » supprimée`, <Icon.Trash size={13} />);
    // Persistance backend (uniquement si id provient du serveur, pas un id temporaire client)
    if (window.ELSIA_API && (id.startsWith("p_"))) {
      window.ELSIA_API.deletePlaylist(id).catch(err => console.error("[api] deletePlaylist échec:", err));
    }
  };

  const toggleActive = (id) => {
    const p = playlists.find(x => x.id === id);
    const willActivate = !p.active;
    setPlaylists(prev => prev.map(pp => ({
      ...pp,
      active: pp.id === id ? !pp.active : (pp.active && pp.id !== id ? false : pp.active)
    })));
    if (!willActivate) {
      pushActivity?.({ who: "Système", action: "a arrêté", target: p.name, type: "pause" });
      flashToast(`Diffusion arrêtée`, <Icon.Pause size={13} />);
    } else {
      pushActivity?.({ who: "Système", action: "a démarré", target: p.name, type: "play" });
      flashToast(`« ${p.name} » en diffusion`, <Icon.Play size={13} />);
      // Persistance backend : seule l'activation est exposée (pas la désactivation pour l'instant)
      if (window.ELSIA_API && id.startsWith("p_")) {
        window.ELSIA_API.activatePlaylist(id).catch(err => console.error("[api] activatePlaylist échec:", err));
      }
    }
  };

  const duplicatePlaylist = (id) => {
    const p = playlists.find(x => x.id === id);
    if (!p) return;
    const copy = { ...p, id: "p" + Date.now(), name: p.name + " (copie)", active: false, zones: JSON.parse(JSON.stringify(p.zones)) };
    setPlaylists(prev => [copy, ...prev]);
    setSelectedId(copy.id);
    pushActivity?.({ action: "a dupliqué la playlist", target: p.name, type: "upload" });
    flashToast(`Playlist dupliquée`, <Icon.Check size={13} />);
  };

  const addItemToZone = (z, media) => {
    let newZones = null;
    setPlaylists(prev => prev.map(p => {
      if (p.id !== selectedId) return p;
      newZones = {
        ...p.zones,
        [z]: [...(p.zones[z] || []), {
          id: newItemId(), mediaId: media.id, title: media.name, type: media.type, duration: media.duration
        }]
      };
      return { ...p, zones: newZones };
    }));
    setPickerZone(null);
    flashToast(`Ajouté à la zone ${z}`, <Icon.Plus size={13} />);
    if (newZones) persistZones(selectedId, newZones);
  };

  const removeItem = (z, itemId) => {
    let newZones = null;
    setPlaylists(prev => prev.map(p => {
      if (p.id !== selectedId) return p;
      newZones = { ...p.zones, [z]: p.zones[z].filter(it => it.id !== itemId) };
      return { ...p, zones: newZones };
    }));
    if (newZones) persistZones(selectedId, newZones);
  };

  const moveItem = (z, itemId, dir) => {
    let newZones = null;
    setPlaylists(prev => prev.map(p => {
      if (p.id !== selectedId) return p;
      const arr = [...p.zones[z]];
      const i = arr.findIndex(it => it.id === itemId);
      if (i < 0) return p;
      const j = i + dir;
      if (j < 0 || j >= arr.length) return p;
      [arr[i], arr[j]] = [arr[j], arr[i]];
      newZones = { ...p.zones, [z]: arr };
      return { ...p, zones: newZones };
    }));
    if (newZones) persistZones(selectedId, newZones);
  };

  const renamePlaylist = (newName) => {
    const t = newName.trim();
    if (!t) return;
    updatePlaylist(selectedId, { name: t });
    setEditingName(false);
  };

  // ----- render ----------
  if (!playlist) {
    return (
      <div className="pane">
        <header className="pane__header">
          <div className="pane__title-group">
            <span className="pane__crumb">Régie · Programmation</span>
            <h1 className="pane__title">Playlists</h1>
          </div>
          <div className="pane__actions">
            <button className="btn btn--primary" onClick={() => setShowNew(true)}><Icon.Plus /> Nouvelle playlist</button>
          </div>
        </header>
        <div className="pane__body" style={{ display: "flex", alignItems: "center", justifyContent: "center", height: "100%" }}>
          <div className="empty-state">
            <Icon.Playlist size={32} />
            <div className="empty-state__title">Aucune playlist</div>
            <div className="empty-state__sub">Créez votre première playlist pour commencer à diffuser.</div>
            <button className="btn btn--primary" onClick={() => setShowNew(true)}><Icon.Plus /> Nouvelle playlist</button>
          </div>
        </div>
        <Modal open={showNew} onClose={() => setShowNew(false)} title="Nouvelle playlist">
          <PlaylistForm onSubmit={createPlaylist} onCancel={() => setShowNew(false)} />
        </Modal>
      </div>
    );
  }

  const items = playlist.zones?.[zone] || [];
  const zoneColor = ZONE_COLORS[zone] || "var(--elsia-navy)";
  const total = totalItems(playlist);

  return (
    <div className="pane">
      <header className="pane__header">
        <div className="pane__title-group">
          <span className="pane__crumb">Régie · Programmation</span>
          <h1 className="pane__title">Playlists</h1>
        </div>
        <div className="pane__actions">
          <button className="btn"><Icon.Download /> Exporter</button>
          <button className="btn btn--primary" onClick={() => setShowNew(true)}><Icon.Plus /> Nouvelle playlist</button>
        </div>
      </header>

      <div className="pane__body" style={{ padding: 0, display: "flex", overflow: "hidden" }}>
        {/* Playlist rail */}
        <div className="playlist-rail">
          <div className="playlist-rail__search">
            <div className="search" style={{ width: "100%", minWidth: 0 }}>
              <Icon.Search />
              <input
                placeholder="Rechercher une playlist…"
                value={search}
                onChange={e => setSearch(e.target.value)}
              />
            </div>
          </div>
          <div className="playlist-rail__list">
            {filteredPlaylists.length === 0 && (
              <div style={{ padding: 16, fontSize: 12, color: "var(--text-muted)", textAlign: "center" }}>
                Aucune playlist trouvée.
              </div>
            )}
            {filteredPlaylists.map(p => {
              const cnt = totalItems(p);
              return (
                <button
                  key={p.id}
                  className={"playlist-row" + (selectedId === p.id ? " is-selected" : "")}
                  onClick={() => setSelectedId(p.id)}
                >
                  <div className="playlist-row__strip" style={{ background: p.color }} />
                  <div style={{ flex: 1, minWidth: 0 }}>
                    <div className="playlist-row__name">
                      {p.name}
                      {p.active && <span className="badge badge--live" style={{ marginLeft: 6 }}>LIVE</span>}
                    </div>
                    <div className="playlist-row__meta">
                      <span>{cnt} tuile{cnt > 1 ? "s" : ""}</span>
                      <span>·</span>
                      <span>{PL_LAYOUTS.find(l => l.id === p.layout)?.label || "Quadrants"}</span>
                    </div>
                    <div className="playlist-row__schedule">
                      <Icon.Calendar size={10} /> {p.schedule}
                    </div>
                  </div>
                </button>
              );
            })}
          </div>
        </div>

        {/* Editor */}
        <div className="playlist-editor">
          <div className="playlist-editor__header">
            <div style={{ flex: 1, minWidth: 0 }}>
              <div style={{ display: "flex", alignItems: "center", gap: 10, flexWrap: "wrap" }}>
                {editingName ? (
                  <input
                    className="form-input"
                    autoFocus
                    defaultValue={playlist.name}
                    style={{ fontFamily: "var(--font-display)", fontSize: 17, fontWeight: 600, maxWidth: 380 }}
                    onBlur={e => renamePlaylist(e.target.value)}
                    onKeyDown={e => {
                      if (e.key === "Enter") renamePlaylist(e.target.value);
                      if (e.key === "Escape") setEditingName(false);
                    }}
                  />
                ) : (
                  <h2 className="playlist-editor__title" onClick={() => setEditingName(true)} title="Cliquer pour renommer">
                    {playlist.name}
                  </h2>
                )}
                {playlist.active && <span className="badge badge--live">En diffusion</span>}
              </div>
              <div className="playlist-editor__meta">
                <span><Icon.Playlist size={11} /> {total} tuile{total > 1 ? "s" : ""}</span>
                <span><Icon.Grid size={11} /> {lay.label}</span>
                <span><Icon.Calendar size={11} /> {playlist.schedule}</span>
              </div>
            </div>
            <div style={{ display: "flex", gap: 6, flexShrink: 0 }}>
              <button
                className="btn btn--sm"
                onClick={() => window.open(`/?mode=viewer&playlist=${playlist.id}&preview=1`, "elsia-preview", "width=1280,height=720,toolbar=no,location=no,menubar=no")}
                title="Aperçu sans diffusion"
              >
                <Icon.Eye size={12} /> Aperçu
              </button>
              <button className="btn btn--sm" onClick={() => duplicatePlaylist(playlist.id)} title="Dupliquer">
                <Icon.Folder size={12} /> Dupliquer
              </button>
              <button className="btn btn--sm" onClick={() => setShowEdit(true)}>
                <Icon.Edit size={12} /> Éditer
              </button>
              <button className="btn btn--sm btn--danger" onClick={() => setConfirmDel(playlist.id)}>
                <Icon.Trash size={12} />
              </button>
              <div className="divider-v" />
              <button
                className={"btn btn--sm " + (playlist.active ? "btn--danger" : "btn--primary")}
                onClick={() => toggleActive(playlist.id)}
              >
                {playlist.active ? <><Icon.Pause size={12} /> Arrêter</> : <><Icon.Play size={12} /> Lancer</>}
              </button>
            </div>
          </div>

          <div className="playlist-editor__body">
            {/* Composition */}
            <div className="card">
              <div className="card__header">
                <div className="card__title">Composition du mur</div>
                <span className="card__title-sub">Cliquez une zone pour éditer ses tuiles</span>
              </div>
              <div style={{ padding: 16 }}>
                <ZonePreview2 layout={playlist.layout} selectedZone={zone} setSelectedZone={setZone} zones={playlist.zones} />
                <div className="layout-picker">
                  <div className="layout-picker__label">Disposition</div>
                  <div className="layout-picker__grid">
                    {PL_LAYOUTS.map(l => (
                      <button
                        key={l.id}
                        className={"layout-picker__item" + (playlist.layout === l.id ? " is-active" : "")}
                        onClick={() => {
                          updatePlaylist(playlist.id, { layout: l.id });
                          setZone(l.zones[0]);
                        }}
                        title={l.label}
                      >
                        <div className="layout-picker__mini" style={{ grid: l.grid }}>
                          {l.zones.map(z => (
                            <div key={z} style={{ gridArea: l.spans?.[z] || "auto" }} />
                          ))}
                        </div>
                        <span className="layout-picker__name">{l.label}</span>
                      </button>
                    ))}
                  </div>
                </div>
                <div className="zone-stats">
                  {lay.zones.map(z => {
                    const cnt = (playlist.zones[z] || []).length;
                    return (
                      <div key={z} className="zone-stat">
                        <span className="zone-stat__letter" style={{ background: ZONE_COLORS[z] + "33", color: ZONE_COLORS[z] }}>{z}</span>
                        <span className="mono">{cnt} tuile{cnt > 1 ? "s" : ""}</span>
                      </div>
                    );
                  })}
                </div>
              </div>
            </div>

            {/* Zone items */}
            <div className="card">
              <div className="card__header">
                <div className="card__title">
                  <span className="zone-stat__letter" style={{ width: 22, height: 22, fontSize: 12, background: zoneColor + "33", color: zoneColor }}>{zone}</span>
                  Tuiles de la zone {zone}
                  <span className="card__title-sub">{items.length} élément{items.length > 1 ? "s" : ""} · lecture séquentielle</span>
                </div>
                <button className="btn btn--sm btn--primary" onClick={() => setPickerZone(zone)}>
                  <Icon.Plus size={12} /> Ajouter
                </button>
              </div>
              <div className="zone-items">
                {items.length === 0 && (
                  <div className="zone-items__empty">
                    <Icon.Playlist size={20} />
                    <div>Aucune tuile dans la zone {zone}.</div>
                    <button className="btn btn--sm btn--primary" onClick={() => setPickerZone(zone)}>
                      <Icon.Plus size={12} /> Ajouter un média
                    </button>
                  </div>
                )}
                {items.map((it, i) => (
                  <div key={it.id} className="zone-item">
                    <span className="zone-item__num mono">{String(i + 1).padStart(2, "0")}</span>
                    <div style={{ width: 52, height: 32, borderRadius: 5, overflow: "hidden", flexShrink: 0 }}>
                      <MediaThumb type={it.type} name={it.title} />
                    </div>
                    <div style={{ flex: 1, minWidth: 0 }}>
                      <div className="zone-item__title">{it.title}</div>
                      <div className="zone-item__meta">
                        <TypeTag type={it.type} />
                        <span style={{ color: "var(--text-muted)" }}>· {it.duration}</span>
                      </div>
                    </div>
                    <div className="zone-item__actions">
                      <button className="btn btn--sm btn--icon btn--ghost" disabled={i === 0} onClick={() => moveItem(zone, it.id, -1)} title="Monter">
                        <Icon.ChevronLeft size={12} style={{ transform: "rotate(90deg)" }} />
                      </button>
                      <button className="btn btn--sm btn--icon btn--ghost" disabled={i === items.length - 1} onClick={() => moveItem(zone, it.id, 1)} title="Descendre">
                        <Icon.ChevronLeft size={12} style={{ transform: "rotate(-90deg)" }} />
                      </button>
                      <button className="btn btn--sm btn--icon btn--ghost" onClick={() => removeItem(zone, it.id)} title="Retirer">
                        <Icon.Trash size={12} />
                      </button>
                    </div>
                  </div>
                ))}
                {items.length > 0 && (
                  <button className="zone-item zone-item--add" onClick={() => setPickerZone(zone)}>
                    <Icon.Plus size={14} />
                    <span style={{ color: "var(--text-muted)", fontSize: 12 }}>Ajouter un média à la zone {zone}…</span>
                  </button>
                )}
              </div>
            </div>
          </div>
        </div>
      </div>

      {/* Modals */}
      <Modal open={showNew} onClose={() => setShowNew(false)} title="Nouvelle playlist">
        <PlaylistForm onSubmit={createPlaylist} onCancel={() => setShowNew(false)} />
      </Modal>

      <Modal open={showEdit} onClose={() => setShowEdit(false)} title="Éditer la playlist">
        <PlaylistForm
          initial={playlist}
          submitLabel="Enregistrer"
          onSubmit={(data) => {
            updatePlaylist(playlist.id, data);
            setShowEdit(false);
            flashToast("Playlist mise à jour", <Icon.Check size={13} />);
          }}
          onCancel={() => setShowEdit(false)}
        />
      </Modal>

      <Modal
        open={!!pickerZone}
        onClose={() => setPickerZone(null)}
        title={`Ajouter un média à la zone ${pickerZone}`}
        size="lg"
      >
        {pickerZone && (
          <MediaPicker
            mediaFiles={mediaFiles}
            onPick={(m) => addItemToZone(pickerZone, m)}
            onClose={() => setPickerZone(null)}
          />
        )}
      </Modal>

      <ConfirmDialog
        open={!!confirmDel}
        title="Supprimer la playlist ?"
        message={`La playlist « ${playlists.find(p => p.id === confirmDel)?.name} » et toutes ses tuiles seront définitivement supprimées. Cette action est irréversible.`}
        danger
        onConfirm={() => deletePlaylist(confirmDel)}
        onCancel={() => setConfirmDel(null)}
      />

      <Toast msg={toast} />
    </div>
  );
}

window.Playlists = Playlists;
window.ConfirmDialog = ConfirmDialog;
window.Toast = Toast;
window.PlaylistForm = PlaylistForm;
