// totem-pages.jsx — Merion Totem · páginas de produto (Painel, Totens, Detalhe, Mídias, Playlists, Config)
// Componentes Merion reaproveitados (globais): Card, CardHeader, Kpi(reproduzido), DataTable, Badge,
// Button, Select, Field, Input, Switch, Progress, Avatar, Sheet, Stepper, Timeline, Alert, Icon, EmptyState, useToast.
// Tokens semânticos apenas. Prefixo de domínio: tot- / totem-.
const { useState: useStateTP, useEffect: useEffectTP, useContext: useContextTP } = React;

/* ---------- helpers ---------- */
function useLiveTotems() {
  // Contexto de tempo real provido por totem-app.jsx ({ totems, refresh }).
  const ctx = useContextTP(window.TotemLiveCtx);
  return ctx || { totems: [], refresh: () => {} };
}

function fmtRelative(iso) {
  if (!iso) return "nunca";
  const then = new Date(iso).getTime();
  if (Number.isNaN(then)) return "—";
  const diff = Math.max(0, Date.now() - then);
  const min = Math.floor(diff / 60000);
  if (min < 1) return "agora mesmo";
  if (min < 60) return `há ${min} min`;
  const h = Math.floor(min / 60);
  if (h < 24) return `há ${h} h`;
  const d = Math.floor(h / 24);
  return `há ${d} d`;
}

function statusVariant(status) { return status === "online" ? "success" : "destructive"; }
function statusLabel(status) { return status === "online" ? "Online" : "Offline"; }
function nowPlayingTitle(t) {
  return (t && t.current_media && t.current_media.title) ? t.current_media.title : "—";
}

/* Mapeia um evento do backend para um item de Timeline (reutilizado inline e no painel "Ver todos"). */
const EVENT_LABELS = {
  connect: "Totem conectado",
  disconnect: "Totem desconectado",
  nowplaying: "Trocou de mídia",
  created: "Totem criado",
  create: "Totem criado",
  command: "Comando enviado",
  "rotate-code": "Código atualizado",
};
function eventToTimelineItem(e, mediaMap) {
  let payload = {};
  try { payload = JSON.parse(e.payload || "{}"); } catch (_) {}
  let desc = null;
  if (payload.mediaId != null) {
    const title = mediaMap && mediaMap[payload.mediaId];
    desc = title ? title : `Mídia #${payload.mediaId}`;
  } else if (payload.command) {
    desc = `Comando: ${payload.command}`;
  }
  return { time: fmtRelative(e.ts), title: EVENT_LABELS[e.type] || e.type, desc };
}

/* KPI — modelado no Kpi de dashboard.jsx (kpi / kpi-top / kpi-ic / kpi-val / kpi-lbl). */
function TotemKpi({ icon, value, label, delta, up = true }) {
  return (
    <Card className="kpi">
      <div className="kpi-top">
        <div className="kpi-ic"><Icon name={icon} /></div>
        {delta && (
          <span className={"kpi-delta " + (up ? "up" : "down")}>
            <Icon name={up ? "chevron-up" : "chevron-down"} style={{ width: 13, height: 13 }} />{delta}
          </span>
        )}
      </div>
      <div className="kpi-val">{value}</div>
      <div className="kpi-lbl">{label}</div>
    </Card>
  );
}

/* ============================================================
   PAINEL — KPIs + grade ao vivo
   ============================================================ */
function PainelPage() {
  const { totems } = useLiveTotems();
  const [mediaCount, setMediaCount] = useStateTP(null);

  useEffectTP(() => {
    let alive = true;
    window.api.media().then((m) => { if (alive) setMediaCount(Array.isArray(m) ? m.length : 0); }).catch(() => {});
    return () => { alive = false; };
  }, []);

  const online = totems.filter((t) => t.status === "online");
  const offline = totems.filter((t) => t.status !== "online");

  return (
    <div className="dash">
      <div className="dash-head">
        <div>
          <div className="ds-kicker">Operação · Tempo real</div>
          <h1>Painel</h1>
          <p>Visão ao vivo da sua rede de totens e do conteúdo em exibição.</p>
        </div>
        <div className="row">
          <Badge variant="success" dot>{online.length} ao vivo</Badge>
        </div>
      </div>

      <div className="kpi-grid">
        <TotemKpi icon="monitor" value={String(online.length)} label="Totens online" up />
        <TotemKpi icon="alarm" value={String(offline.length)} label="Totens offline" up={false} />
        <TotemKpi icon="grid" value={String(totems.length)} label="Totens no total" up />
        <TotemKpi icon="image" value={mediaCount == null ? "…" : String(mediaCount)} label="Mídias ativas" up />
      </div>

      <Card>
        <CardHeader title="Grade ao vivo" desc="O que cada totem está exibindo agora"
          action={<Badge variant="soft" dot>{totems.length} totens</Badge>} />
        <div className="card-body">
          {totems.length === 0 ? (
            <EmptyState icon="monitor" title="Nenhum totem ainda" desc="Cadastre um totem para começar a transmitir conteúdo." />
          ) : (
            <div className="totem-grid">
              {totems.map((t) => (
                <div className="totem-live-card" key={t.id} data-status={t.status}>
                  <div className="totem-live-head">
                    <span className={"badge-dot tot-status-dot"} data-status={t.status} />
                    <div className="totem-live-name">{t.name}</div>
                    <Badge variant={statusVariant(t.status)} dot>{statusLabel(t.status)}</Badge>
                  </div>
                  <div className="totem-live-loc">{t.location || "Sem local definido"}</div>
                  <div className="totem-live-now">
                    <Icon name={t.status === "online" ? "image" : "alarm"} />
                    <span className="totem-live-now-title">
                      {t.status === "online" ? nowPlayingTitle(t) : "Sem sinal"}
                    </span>
                  </div>
                  <div className="totem-live-foot">
                    <span className="cell-muted">Visto {fmtRelative(t.last_seen_at)}</span>
                    {t.playlist_name && <Badge variant="soft">{t.playlist_name}</Badge>}
                  </div>
                </div>
              ))}
            </div>
          )}
        </div>
      </Card>
    </div>
  );
}

/* ============================================================
   TOTEM DETALHE — preview ao vivo, pareamento, comandos, timeline
   ============================================================ */
function TotemDetalhe({ totemId, liveTotem, onBack }) {
  const t = useToast();
  const { refresh } = useLiveTotems();
  const [detail, setDetail] = useStateTP(null);
  const [playlists, setPlaylists] = useStateTP([]);
  const [code, setCode] = useStateTP("");
  const [saving, setSaving] = useStateTP(false);
  const [showEvents, setShowEvents] = useStateTP(false);

  const load = React.useCallback(() => {
    window.api.totem(totemId).then((d) => {
      setDetail(d);
      if (d && d.pairing_code) setCode(d.pairing_code);
    }).catch(() => {});
  }, [totemId]);

  useEffectTP(() => {
    load();
    window.api.playlists().then((p) => setPlaylists(Array.isArray(p) ? p : [])).catch(() => {});
  }, [load]);

  // Mescla atualizações vindas do socket (live) sobre o detalhe carregado.
  const totem = React.useMemo(() => {
    if (!detail) return liveTotem || null;
    if (liveTotem) {
      return { ...detail, status: liveTotem.status, last_seen_at: liveTotem.last_seen_at, current_media: liveTotem.current_media, current_media_id: liveTotem.current_media_id, playlist_name: liveTotem.playlist_name };
    }
    return detail;
  }, [detail, liveTotem]);

  if (!totem) {
    return (
      <div className="dash" style={{ maxWidth: 980 }}>
        <button className="tx-back" onClick={onBack}><Icon name="chevron-left" />Totens</button>
        <Card className="card-pad"><Skeleton h={120} /></Card>
      </div>
    );
  }

  // "Ver todos" abre a página completa de eventos (com filtros).
  if (showEvents) {
    return <EventosPage totem={totem} onBack={() => setShowEvents(false)} />;
  }

  const totemUrl = location.origin + "/totem";
  const pairUrl = totemUrl + "?id=" + totem.id + "&code=" + encodeURIComponent(code || totem.pairing_code || "");
  const orientation = totem.orientation || "landscape";
  const playlistOptions = [{ value: "", label: "Sem playlist" }].concat(
    playlists.map((p) => ({ value: String(p.id), label: p.name + (p.item_count != null ? ` · ${p.item_count} itens` : "") }))
  );
  const currentPlaylistValue = totem.playlist_id != null ? String(totem.playlist_id) : "";

  const events = Array.isArray(detail && detail.events) ? detail.events : [];
  const eventsTotal = detail && detail.events_total != null ? detail.events_total : events.length;

  const changePlaylist = (val) => {
    const playlist_id = val === "" ? null : Number(val);
    setSaving(true);
    window.api.updateTotem(totem.id, { playlist_id }).then(() => {
      t({ title: "Playlist atualizada", icon: "check-circle" });
      load(); refresh();
    }).catch(() => t({ title: "Falha ao atualizar", icon: "alert-circle" }))
      .finally(() => setSaving(false));
  };

  const changeOrientation = (val) => {
    setSaving(true);
    window.api.updateTotem(totem.id, { orientation: val }).then(() => {
      t({ title: "Orientação atualizada", desc: val === "portrait" ? "Retrato (vertical)" : "Paisagem (horizontal)", icon: "check-circle" });
      load(); refresh();
    }).catch(() => t({ title: "Falha ao atualizar", icon: "alert-circle" }))
      .finally(() => setSaving(false));
  };

  const rotate = () => {
    window.api.rotateCode(totem.id).then((res) => {
      const newCode = res && res.pairing_code;
      if (newCode) setCode(newCode);
      t({ title: "Novo código de pareamento", desc: newCode, icon: "refresh" });
      load();
    }).catch(() => t({ title: "Falha ao gerar código", icon: "alert-circle" }));
  };

  const sendCommand = (command) => {
    window.api.command(totem.id, command).then(() => {
      t({ title: "Comando enviado", desc: command, icon: "send" });
    }).catch(() => t({ title: "Totem offline", desc: "Não foi possível enviar o comando.", icon: "alert-circle" }));
  };

  const copy = (text, label) => {
    try { navigator.clipboard.writeText(text); } catch (_) {}
    t({ title: label + " copiado", icon: "copy" });
  };

  return (
    <div className="dash" style={{ maxWidth: 980 }}>
      <button className="tx-back" onClick={onBack}><Icon name="chevron-left" />Totens</button>
      <div className="dash-head" style={{ alignItems: "center" }}>
        <div>
          <div className="ds-kicker">Totem</div>
          <h1 style={{ fontSize: 24 }}>{totem.name}</h1>
          <p>{totem.location || "Sem local definido"} · visto {fmtRelative(totem.last_seen_at)}</p>
        </div>
        <div className="row">
          <Badge variant={statusVariant(totem.status)} dot>{statusLabel(totem.status)}</Badge>
          <Badge variant="soft"><Icon name={orientation === "portrait" ? "smartphone" : "monitor"} style={{ width: 13, height: 13 }} /> {orientation === "portrait" ? "Retrato" : "Paisagem"}</Badge>
          <Button variant="outline" size="sm" icon="refresh" onClick={() => sendCommand("reload")}>Recarregar</Button>
        </div>
      </div>

      <div className="tx-grid">
        {/* Preview tocando agora (ao vivo) */}
        <Card className="card-pad tot-preview-card">
          <div className="well-label">Tocando agora</div>
          <div className="tot-preview" data-orientation={orientation} data-empty={!(totem.status === "online" && totem.current_media)}>
            {totem.status === "online" && totem.current_media ? (
              totem.current_media.type === "video"
                ? <video className="tot-preview-media" src={totem.current_media.url} muted autoPlay loop playsInline />
                : <img className="tot-preview-media" src={totem.current_media.url} alt={totem.current_media.title} />
            ) : (
              <div className="tot-preview-empty"><Icon name="monitor" /><span>{totem.status === "online" ? "Sem mídia" : "Totem offline"}</span></div>
            )}
          </div>
          <div className="tot-preview-cap">
            <span className="cell-strong">{nowPlayingTitle(totem)}</span>
            {totem.current_media && <Badge variant="soft">{totem.current_media.type}</Badge>}
          </div>
        </Card>

        {/* Pareamento */}
        <Card className="card-pad">
          <div className="well-label">Pareamento</div>
          <div className="tot-pair-ids">
            <div className="tot-pair-id">
              <span className="tot-pair-id-lbl">ID do totem</span>
              <span className="tot-pair-id-val mono">#{totem.id}</span>
            </div>
            <div className="tot-pair-id">
              <span className="tot-pair-id-lbl">Código</span>
              <span className="tot-pair-id-val tot-pair-code">{code || totem.pairing_code || "------"}</span>
            </div>
          </div>
          <div className="row" style={{ gap: 8, marginTop: 14 }}>
            <Button variant="outline" size="sm" icon="copy" onClick={() => copy(code || totem.pairing_code, "Código")}>Copiar código</Button>
            <Button variant="ghost" size="sm" icon="refresh" onClick={rotate}>Gerar novo</Button>
          </div>
          <div className="card-sep" style={{ margin: "16px 0" }} />
          <div className="well-label">Link de pareamento</div>
          <div className="row" style={{ gap: 8 }}>
            <Input readOnly value={pairUrl} className="mono" style={{ flex: 1 }} />
            <Button variant="brand" size="sm" icon="copy" onClick={() => copy(pairUrl, "Link")} />
          </div>
          <div className="tot-pair-hint"><Icon name="info" /> Abra este link no aparelho do totem — ele pareia sozinho, sem digitar ID nem código.</div>
        </Card>
      </div>

      <div className="dash-cols" style={{ marginTop: 16 }}>
        <Card className="card-pad">
          <CardHeader title="Configuração" desc="Conteúdo e comandos" />
          <div className="card-body stack-sm" style={{ gap: 16, padding: "16px 0 0" }}>
            <Field label="Playlist em exibição">
              <Select value={currentPlaylistValue} onChange={changePlaylist} options={playlistOptions} placeholder="Sem playlist" />
            </Field>
            <Field label="Orientação da tela" hint="Aplicada ao totem conectado em tempo real.">
              <Select value={orientation} onChange={changeOrientation}
                options={[{ value: "landscape", label: "Paisagem (horizontal)" }, { value: "portrait", label: "Retrato (vertical)" }]} />
            </Field>
            <div className="row" style={{ gap: 10 }}>
              <Button variant="brand" icon="refresh" disabled={totem.status !== "online"} onClick={() => sendCommand("reload")}>Recarregar conteúdo</Button>
              {saving && <Spinner size="sm" />}
            </div>
            {totem.status !== "online" && <Alert variant="brand" title="Totem offline">Comandos serão entregues quando o totem reconectar.</Alert>}
          </div>
        </Card>

        <Card className="card-pad">
          <CardHeader title="Eventos" desc="Últimos eventos do totem"
            action={<Button variant="ghost" size="sm" icon="refresh" onClick={load} />} />
          <div className="card-body" style={{ padding: "12px 0 0" }}>
            {events.length === 0
              ? <EmptyState icon="timeline" title="Sem eventos" desc="A atividade do totem aparecerá aqui." />
              : <Timeline items={events.slice(0, 5).map(eventToTimelineItem)} />}
          </div>
          {eventsTotal > 5 && (
            <div className="card-footer" style={{ padding: "14px 0 0", justifyContent: "center" }}>
              <Button variant="outline" size="sm" iconRight="arrowRight" onClick={() => setShowEvents(true)}>
                Ver todos ({eventsTotal})
              </Button>
            </div>
          )}
        </Card>
      </div>
    </div>
  );
}

/* ---------- Eventos (todos) — página completa com filtros ---------- */
function EventosPage({ totem, onBack }) {
  const PAGE = 12;
  const [page, setPage] = useStateTP(1);
  const [type, setType] = useStateTP("");
  const [mediaId, setMediaId] = useStateTP("");
  const [from, setFrom] = useStateTP("");
  const [to, setTo] = useStateTP("");
  const [media, setMedia] = useStateTP([]);
  const [events, setEvents] = useStateTP([]);
  const [count, setCount] = useStateTP(0);
  const [loading, setLoading] = useStateTP(false);

  // Carrega a lista de mídias para o filtro (e para resolver títulos na timeline).
  useEffectTP(() => {
    window.api.media().then((m) => setMedia(Array.isArray(m) ? m : [])).catch(() => {});
  }, []);

  // Qualquer mudança de filtro volta para a primeira página.
  useEffectTP(() => { setPage(1); }, [type, mediaId, from, to]);

  useEffectTP(() => {
    let alive = true;
    setLoading(true);
    window.api.totemEvents(totem.id, { limit: PAGE, offset: (page - 1) * PAGE, type, mediaId, from, to })
      .then((res) => { if (!alive) return; setEvents(res.events || []); setCount(res.total || 0); })
      .catch(() => {})
      .finally(() => { if (alive) setLoading(false); });
    return () => { alive = false; };
  }, [totem.id, page, type, mediaId, from, to]);

  const mediaMap = {};
  media.forEach((m) => { mediaMap[m.id] = m.title; });
  const totalPages = Math.max(1, Math.ceil((count || 0) / PAGE));

  const typeOptions = [
    { value: "", label: "Todos os eventos" },
    { value: "nowplaying", label: "Trocou de mídia" },
    { value: "connect", label: "Conectado" },
    { value: "disconnect", label: "Desconectado" },
    { value: "command", label: "Comando enviado" },
    { value: "rotate-code", label: "Código atualizado" },
    { value: "created", label: "Totem criado" },
  ];
  const mediaOptions = [{ value: "", label: "Todas as mídias" }].concat(
    media.map((m) => ({ value: String(m.id), label: m.title }))
  );
  const hasFilters = !!(type || mediaId || from || to);
  const clearFilters = () => { setType(""); setMediaId(""); setFrom(""); setTo(""); };

  return (
    <div className="dash tot-events-page" style={{ maxWidth: 980 }}>
      <button className="tx-back" onClick={onBack}><Icon name="chevron-left" />{totem.name}</button>
      <div className="dash-head" style={{ alignItems: "center" }}>
        <div>
          <div className="ds-kicker">Totem · {totem.name}</div>
          <h1>Eventos</h1>
          <p>Histórico completo de atividade do totem.</p>
        </div>
        <div className="row"><Badge variant="soft" dot>{count} evento(s)</Badge></div>
      </div>

      <Card className="card-pad" style={{ marginBottom: 16 }}>
        <div className="tot-events-filters">
          <Field label="Evento"><Select value={type} onChange={setType} options={typeOptions} /></Field>
          <Field label="Mídia"><Select value={mediaId} onChange={setMediaId} options={mediaOptions} /></Field>
          <Field label="De"><Input type="date" value={from} onChange={(e) => setFrom(e.target.value)} /></Field>
          <Field label="Até"><Input type="date" value={to} onChange={(e) => setTo(e.target.value)} /></Field>
          {hasFilters && <Button variant="ghost" icon="x" className="tot-events-clear" onClick={clearFilters}>Limpar</Button>}
        </div>
      </Card>

      <Card className="card-pad">
        <div className="tot-events-list">
          {loading
            ? <div className="stack-sm" style={{ gap: 10 }}>{[0, 1, 2, 3, 4, 5].map((i) => <Skeleton key={i} h={46} />)}</div>
            : events.length === 0
              ? <EmptyState icon="timeline" title="Nenhum evento" desc={hasFilters ? "Nenhum evento para os filtros selecionados." : "A atividade do totem aparecerá aqui."} />
              : <Timeline items={events.map((e) => eventToTimelineItem(e, mediaMap))} />}
        </div>
        {totalPages > 1 && (
          <div className="card-footer tot-events-pager" style={{ justifyContent: "space-between", padding: "16px 0 0" }}>
            <span className="cell-muted" style={{ fontSize: 13 }}>Página {page} de {totalPages}</span>
            <Pagination page={page} total={totalPages} onChange={setPage} />
          </div>
        )}
      </Card>
    </div>
  );
}

/* ============================================================
   TOTENS — DataTable + abre detalhe + Novo totem
   ============================================================ */
function TotensPage() {
  const { totems, refresh } = useLiveTotems();
  const [selected, setSelected] = useStateTP(null);
  const [novoOpen, setNovoOpen] = useStateTP(false);

  const liveSelected = selected != null ? totems.find((t) => t.id === selected) : null;

  if (selected != null) {
    return <TotemDetalhe totemId={selected} liveTotem={liveSelected} onBack={() => { setSelected(null); refresh(); }} />;
  }

  const columns = [
    { key: "name", label: "Nome", sortable: true, render: (r) => (
      <span className="row" style={{ gap: 10 }}>
        <span className="tot-status-dot badge-dot" data-status={r.status} />
        <span className="cell-strong">{r.name}</span>
        <span className="tot-id-chip mono">#{r.id}</span>
      </span>
    ) },
    { key: "location", label: "Local", render: (r) => <span className="cell-muted">{r.location || "—"}</span> },
    { key: "orientation", label: "Orientação", render: (r) => (
      <span className="row cell-muted" style={{ gap: 6 }}>
        <Icon name={(r.orientation || "landscape") === "portrait" ? "smartphone" : "monitor"} style={{ width: 14, height: 14 }} />
        {(r.orientation || "landscape") === "portrait" ? "Retrato" : "Paisagem"}
      </span>
    ) },
    { key: "status", label: "Status", render: (r) => <Badge variant={statusVariant(r.status)} dot>{statusLabel(r.status)}</Badge> },
    { key: "now", label: "Tocando agora", render: (r) => (
      r.status === "online" && r.current_media
        ? <span className="row" style={{ gap: 8 }}><Icon name={r.current_media.type === "video" ? "carousel" : "image"} style={{ color: "var(--muted-foreground)" }} />{r.current_media.title}</span>
        : <span className="cell-muted">—</span>
    ) },
    { key: "last_seen_at", label: "Visto por último", render: (r) => <span className="cell-muted mono">{fmtRelative(r.last_seen_at)}</span> },
    { key: "go", label: "", render: () => <Icon name="chevron-right" style={{ color: "var(--muted-foreground)" }} /> },
  ];

  const rows = totems.map((t) => ({ ...t, id: t.id }));

  return (
    <div className="dash">
      <div className="dash-head">
        <div>
          <div className="ds-kicker">Operação</div>
          <h1>Totens</h1>
          <p>Gerencie seus dispositivos. Clique numa linha para ver o detalhe.</p>
        </div>
        <div className="row">
          <Button variant="outline" icon="refresh" onClick={refresh}>Atualizar</Button>
          <Button variant="brand" icon="plus" onClick={() => setNovoOpen(true)}>Novo totem</Button>
        </div>
      </div>

      <Card>
        <div className="card-table">
          {rows.length === 0
            ? <EmptyState icon="monitor" title="Nenhum totem cadastrado" desc="Crie seu primeiro totem para começar." action={<Button variant="outline" icon="plus" onClick={() => setNovoOpen(true)}>Novo totem</Button>} />
            : <DataTable columns={columns} rows={rows} onRowClick={(r) => setSelected(r.id)} />}
        </div>
      </Card>

      <NovoTotemSheet open={novoOpen} onClose={() => setNovoOpen(false)} onCreated={() => { refresh(); }} />
    </div>
  );
}

/* ---------- Novo totem (Sheet, modelado em NovoContratoSheet) ---------- */
function NovoTotemSheet({ open, onClose, onCreated }) {
  const t = useToast();
  const [name, setName] = useStateTP("");
  const [loc, setLoc] = useStateTP("");
  const [playlistId, setPlaylistId] = useStateTP("");
  const [orientation, setOrientation] = useStateTP("landscape");
  const [playlists, setPlaylists] = useStateTP([]);
  const [created, setCreated] = useStateTP(null);
  const [busy, setBusy] = useStateTP(false);

  useEffectTP(() => {
    if (!open) return;
    setName(""); setLoc(""); setPlaylistId(""); setOrientation("landscape"); setCreated(null); setBusy(false);
    window.api.playlists().then((p) => setPlaylists(Array.isArray(p) ? p : [])).catch(() => {});
  }, [open]);

  const playlistOptions = [{ value: "", label: "Sem playlist" }].concat(
    playlists.map((p) => ({ value: String(p.id), label: p.name }))
  );

  const submit = () => {
    if (!name.trim()) { t({ title: "Informe o nome do totem", icon: "alert-circle" }); return; }
    setBusy(true);
    window.api.createTotem({ name: name.trim(), location: loc.trim(), playlist_id: playlistId === "" ? null : Number(playlistId), orientation })
      .then((totem) => {
        setCreated(totem);
        onCreated && onCreated();
        t({ title: "Totem criado", desc: totem.name, icon: "check-circle" });
      })
      .catch(() => t({ title: "Falha ao criar totem", icon: "alert-circle" }))
      .finally(() => setBusy(false));
  };

  return (
    <Sheet open={open} onClose={onClose} title="Novo totem" desc="Cadastre um dispositivo e obtenha o código de pareamento"
      footer={created
        ? <Button variant="brand" icon="check" onClick={onClose}>Concluir</Button>
        : <>
            <Button variant="ghost" onClick={onClose}>Cancelar</Button>
            <Button variant="brand" icon="plus" disabled={busy} onClick={submit}>Criar totem</Button>
          </>}>
      {created ? (
        <div className="stack-sm" style={{ gap: 16 }}>
          <Alert variant="success" title="Totem criado com sucesso">Abra o link de pareamento no aparelho — ele conecta sozinho.</Alert>
          <div className="tot-pair-block">
            <div className="tot-pair-ids">
              <div className="tot-pair-id"><span className="tot-pair-id-lbl">ID do totem</span><span className="tot-pair-id-val mono">#{created.id}</span></div>
              <div className="tot-pair-id"><span className="tot-pair-id-lbl">Código</span><span className="tot-pair-id-val tot-pair-code">{created.pairing_code}</span></div>
            </div>
          </div>
          <Field label="Link de pareamento (abra no totem)">
            <Input readOnly value={location.origin + "/totem?id=" + created.id + "&code=" + encodeURIComponent(created.pairing_code)} className="mono" />
          </Field>
        </div>
      ) : (
        <div className="stack-sm" style={{ gap: 16 }}>
          <Field label="Nome do totem"><Input value={name} placeholder="ex: Totem Recepção" onChange={(e) => setName(e.target.value)} /></Field>
          <Field label="Local"><Input icon="building" value={loc} placeholder="ex: Matriz - Recepção" onChange={(e) => setLoc(e.target.value)} /></Field>
          <Field label="Playlist inicial"><Select value={playlistId} onChange={setPlaylistId} options={playlistOptions} placeholder="Sem playlist" /></Field>
          <Field label="Orientação da tela">
            <Select value={orientation} onChange={setOrientation}
              options={[{ value: "landscape", label: "Paisagem (horizontal)" }, { value: "portrait", label: "Retrato (vertical)" }]} />
          </Field>
        </div>
      )}
    </Sheet>
  );
}

/* ============================================================
   MÍDIAS — grade + adicionar por URL
   ============================================================ */
function MidiasPage() {
  const t = useToast();
  const [media, setMedia] = useStateTP([]);
  const [loading, setLoading] = useStateTP(true);
  const [open, setOpen] = useStateTP(false);
  const [mode, setMode] = useStateTP("upload"); // upload | url
  const [storageMode, setStorageMode] = useStateTP(null); // spaces | local
  const [file, setFile] = useStateTP(null);
  const [upTitle, setUpTitle] = useStateTP("");
  const [upDuration, setUpDuration] = useStateTP(8);
  const [progress, setProgress] = useStateTP(0);
  const [form, setForm] = useStateTP({ title: "", type: "image", url: "", duration_sec: 8 });
  const [busy, setBusy] = useStateTP(false);
  const fileRef = React.useRef(null);

  const hasProcessing = media.some((m) => m.status === "processing");

  const load = React.useCallback(() => {
    window.api.media().then((m) => setMedia(Array.isArray(m) ? m : [])).catch(() => {}).finally(() => setLoading(false));
  }, []);
  useEffectTP(() => {
    setLoading(true); load();
    window.api.config().then((c) => setStorageMode(c && c.uploads && c.uploads.mode)).catch(() => {});
  }, [load]);
  // Atualiza a lista periodicamente enquanto houver mídia em processamento.
  useEffectTP(() => {
    if (!hasProcessing) return;
    const id = setInterval(load, 3000);
    return () => clearInterval(id);
  }, [hasProcessing, load]);

  const openSheet = () => {
    setMode("upload"); setFile(null); setUpTitle(""); setUpDuration(8); setProgress(0);
    setForm({ title: "", type: "image", url: "", duration_sec: 8 });
    setOpen(true);
  };

  const fileType = file ? (String(file.type).startsWith("video") ? "video" : "image") : null;

  const submitUpload = () => {
    if (!file) { t({ title: "Selecione um arquivo", icon: "alert-circle" }); return; }
    setBusy(true); setProgress(0);
    const meta = { title: upTitle.trim(), type: fileType };
    if (fileType === "image") meta.duration_sec = Number(upDuration) || 8;
    window.api.uploadMedia(file, meta, (p) => setProgress(p))
      .then(() => {
        t({ title: fileType === "video" ? "Vídeo enviado — convertendo…" : "Mídia enviada", icon: "check-circle" });
        setOpen(false); setLoading(true); load();
      })
      .catch((e) => t({ title: "Falha no upload", desc: e.message, icon: "alert-circle" }))
      .finally(() => setBusy(false));
  };

  const submitUrl = () => {
    if (!form.url.trim()) { t({ title: "Informe a URL da mídia", icon: "alert-circle" }); return; }
    setBusy(true);
    const body = { title: form.title.trim() || "Sem título", type: form.type, url: form.url.trim() };
    if (form.type === "image") body.duration_sec = Number(form.duration_sec) || 8;
    window.api.createMedia(body)
      .then(() => { t({ title: "Mídia adicionada", icon: "check-circle" }); setOpen(false); load(); })
      .catch(() => t({ title: "Falha ao adicionar mídia", icon: "alert-circle" }))
      .finally(() => setBusy(false));
  };

  const remove = (m) => {
    window.api.deleteMedia(m.id).then(() => { t({ title: "Mídia removida", icon: "trash" }); load(); })
      .catch(() => t({ title: "Falha ao remover", icon: "alert-circle" }));
  };

  return (
    <div className="dash">
      <div className="dash-head">
        <div>
          <div className="ds-kicker">Conteúdo</div>
          <h1>Mídias</h1>
          <p>Biblioteca de imagens e vídeos exibidos nos totens.</p>
        </div>
        <div className="row">
          <Button variant="outline" icon="refresh" onClick={() => { setLoading(true); load(); }}>Atualizar</Button>
          <Button variant="brand" icon="upload" onClick={openSheet}>Adicionar mídia</Button>
        </div>
      </div>

      {loading ? (
        <div className="media-grid">{[0, 1, 2, 3].map((i) => <Card key={i} className="card-pad"><Skeleton h={140} /></Card>)}</div>
      ) : media.length === 0 ? (
        <Card><EmptyState icon="image" title="Sem mídias" desc="Envie um arquivo (imagem ou vídeo) ou adicione por URL." action={<Button variant="outline" icon="upload" onClick={openSheet}>Adicionar mídia</Button>} /></Card>
      ) : (
        <div className="media-grid">
          {media.map((m) => (
            <Card key={m.id} className="media-card" data-status={m.status}>
              <div className="media-thumb" data-type={m.type}>
                {m.status === "processing" ? (
                  <div className="media-processing"><Icon name="loader" /><span>Convertendo…</span></div>
                ) : m.status === "error" ? (
                  <div className="media-processing media-failed"><Icon name="alert-circle" /><span>Falhou</span></div>
                ) : m.type === "video" ? (
                  <video src={m.url} muted preload="metadata" />
                ) : (
                  <img src={m.url} alt={m.title} loading="lazy" />
                )}
                <Badge variant="soft" className="media-type-badge">{m.type === "video" ? "Vídeo" : "Imagem"}</Badge>
              </div>
              <div className="media-meta">
                <div className="media-title">{m.title}</div>
                <div className="row" style={{ justifyContent: "space-between" }}>
                  <span className="cell-muted">
                    {m.status === "processing" ? <span><Icon name="loader" /> processando</span>
                      : m.status === "error" ? <span style={{ color: "var(--destructive)" }}>erro na conversão</span>
                      : <span><Icon name="clock" /> {m.duration_sec}s</span>}
                  </span>
                  <Button variant="ghost" size="sm" icon="trash" onClick={() => remove(m)} />
                </div>
              </div>
            </Card>
          ))}
        </div>
      )}

      <Sheet open={open} onClose={() => { if (!busy) setOpen(false); }} title="Adicionar mídia" desc="Envie um arquivo ou aponte uma URL"
        footer={<>
          <Button variant="ghost" disabled={busy} onClick={() => setOpen(false)}>Cancelar</Button>
          {mode === "upload"
            ? <Button variant="brand" icon="upload" disabled={busy || !file} onClick={submitUpload}>{busy ? "Enviando " + progress + "%" : "Enviar"}</Button>
            : <Button variant="brand" icon="plus" disabled={busy} onClick={submitUrl}>Adicionar</Button>}
        </>}>
        <div className="tabs-list" style={{ marginBottom: 18 }}>
          <button className="tab" data-active={mode === "upload"} onClick={() => setMode("upload")}><Icon name="upload" /> Enviar arquivo</button>
          <button className="tab" data-active={mode === "url"} onClick={() => setMode("url")}><Icon name="globe" /> Por URL</button>
        </div>

        {mode === "upload" ? (
          <div className="stack-sm" style={{ gap: 16 }}>
            <input ref={fileRef} type="file" accept="image/*,video/*" style={{ display: "none" }}
              onChange={(e) => { const f = e.target.files && e.target.files[0]; setFile(f || null); if (f && !upTitle.trim()) setUpTitle(f.name.replace(/\.[^.]+$/, "")); }} />
            <button type="button" className="media-drop" data-has={!!file} onClick={() => fileRef.current && fileRef.current.click()}>
              <Icon name={file ? (fileType === "video" ? "video" : "image") : "upload"} />
              <span className="media-drop-main">{file ? file.name : "Escolher imagem ou vídeo"}</span>
              <span className="media-drop-sub">{file ? (Math.max(1, Math.round(file.size / 1048576)) + " MB · " + (fileType === "video" ? "vídeo" : "imagem")) : "MP4, MOV, JPG, PNG… (até 1 GB)"}</span>
            </button>
            <Field label="Título"><Input value={upTitle} placeholder="ex: Vídeo de inauguração" onChange={(e) => setUpTitle(e.target.value)} /></Field>
            {fileType === "image" && (
              <Field label="Duração (segundos)" hint="Quanto tempo a imagem fica na tela.">
                <Input type="number" min="1" value={upDuration} onChange={(e) => setUpDuration(e.target.value)} />
              </Field>
            )}
            {fileType === "video" && <Alert variant="brand" title="Vídeo">A duração é detectada automaticamente e o arquivo é convertido para H.264 (toca em qualquer totem).</Alert>}
            {busy && <Progress value={progress} />}
            <div className="media-storage-hint"><Icon name={storageMode === "spaces" ? "globe" : "building"} /> {storageMode === "spaces" ? "Enviando para o seu DigitalOcean Spaces." : "Armazenamento local (configure o Spaces para usar a nuvem)."}</div>
          </div>
        ) : (
          <div className="stack-sm" style={{ gap: 16 }}>
            <Field label="Título"><Input value={form.title} placeholder="ex: Banner promo" onChange={(e) => setForm({ ...form, title: e.target.value })} /></Field>
            <Field label="Tipo">
              <Select value={form.type} onChange={(v) => setForm({ ...form, type: v })}
                options={[{ value: "image", label: "Imagem" }, { value: "video", label: "Vídeo" }]} />
            </Field>
            <Field label="URL"><Input icon="globe" value={form.url} placeholder="https://..." className="mono" onChange={(e) => setForm({ ...form, url: e.target.value })} /></Field>
            {form.type === "image" && (
              <Field label="Duração (segundos)"><Input type="number" min="1" value={form.duration_sec} onChange={(e) => setForm({ ...form, duration_sec: e.target.value })} /></Field>
            )}
            {form.type === "video" && <div className="media-storage-hint"><Icon name="info" /> A duração do vídeo é detectada automaticamente.</div>}
            {form.url && (
              <div className="media-preview-box">
                {form.type === "video" ? <video src={form.url} muted controls /> : <img src={form.url} alt="" />}
              </div>
            )}
          </div>
        )}
      </Sheet>
    </div>
  );
}

/* ============================================================
   PLAYLISTS — lista + criar/editar itens
   ============================================================ */
function PlaylistsPage() {
  const t = useToast();
  const [playlists, setPlaylists] = useStateTP([]);
  const [media, setMedia] = useStateTP([]);
  const [loading, setLoading] = useStateTP(true);
  const [editing, setEditing] = useStateTP(null); // {id?, name, mediaIds:[]}
  const [busy, setBusy] = useStateTP(false);

  const load = React.useCallback(() => {
    setLoading(true);
    Promise.all([window.api.playlists(), window.api.media()])
      .then(([pl, md]) => { setPlaylists(Array.isArray(pl) ? pl : []); setMedia(Array.isArray(md) ? md : []); })
      .catch(() => {})
      .finally(() => setLoading(false));
  }, []);
  useEffectTP(() => { load(); }, [load]);

  const openNew = () => setEditing({ name: "", mediaIds: [] });
  const openEdit = (pl) => {
    window.api.playlist(pl.id).then((full) => {
      setEditing({ id: full.id, name: full.name, mediaIds: (full.items || []).map((m) => m.id) });
    }).catch(() => setEditing({ id: pl.id, name: pl.name, mediaIds: [] }));
  };

  const toggleItem = (mediaId) => {
    setEditing((cur) => {
      const has = cur.mediaIds.includes(mediaId);
      return { ...cur, mediaIds: has ? cur.mediaIds.filter((x) => x !== mediaId) : [...cur.mediaIds, mediaId] };
    });
  };

  const save = () => {
    if (!editing.name.trim()) { t({ title: "Informe o nome da playlist", icon: "alert-circle" }); return; }
    setBusy(true);
    const done = () => { t({ title: "Playlist salva", icon: "check-circle" }); setEditing(null); load(); };
    const fail = () => t({ title: "Falha ao salvar", icon: "alert-circle" });
    const after = () => setBusy(false);
    if (editing.id) {
      window.api.updatePlaylist(editing.id, { name: editing.name.trim(), mediaIds: editing.mediaIds })
        .then(done).catch(fail).finally(after);
    } else {
      // cria e em seguida define os itens
      window.api.createPlaylist({ name: editing.name.trim() })
        .then((pl) => editing.mediaIds.length
          ? window.api.updatePlaylist(pl.id, { mediaIds: editing.mediaIds })
          : pl)
        .then(done).catch(fail).finally(after);
    }
  };

  const remove = (pl) => {
    window.api.deletePlaylist(pl.id).then(() => { t({ title: "Playlist removida", icon: "trash" }); load(); })
      .catch(() => t({ title: "Falha ao remover", icon: "alert-circle" }));
  };

  if (editing) {
    return (
      <div className="dash" style={{ maxWidth: 920 }}>
        <button className="tx-back" onClick={() => setEditing(null)}><Icon name="chevron-left" />Playlists</button>
        <div className="dash-head">
          <div>
            <div className="ds-kicker">Conteúdo</div>
            <h1>{editing.id ? "Editar playlist" : "Nova playlist"}</h1>
            <p>Defina o nome e selecione as mídias na ordem de exibição.</p>
          </div>
          <div className="row">
            <Button variant="ghost" onClick={() => setEditing(null)}>Cancelar</Button>
            <Button variant="brand" icon="check" disabled={busy} onClick={save}>Salvar</Button>
          </div>
        </div>

        <Card className="card-pad" style={{ marginBottom: 16 }}>
          <Field label="Nome da playlist"><Input value={editing.name} placeholder="ex: Campanha Padrão" onChange={(e) => setEditing({ ...editing, name: e.target.value })} /></Field>
        </Card>

        <Card>
          <CardHeader title="Mídias" desc={`${editing.mediaIds.length} selecionadas`} />
          <div className="card-body">
            {media.length === 0 ? (
              <EmptyState icon="image" title="Sem mídias" desc="Adicione mídias na aba Mídias primeiro." />
            ) : (
              <div className="playlist-pick">
                {media.map((m) => {
                  const idx = editing.mediaIds.indexOf(m.id);
                  return (
                    <button key={m.id} type="button" className="playlist-pick-item" data-on={idx >= 0} onClick={() => toggleItem(m.id)}>
                      <span className="playlist-pick-thumb" data-type={m.type}>
                        {m.type === "video" ? <Icon name="carousel" /> : <img src={m.url} alt="" loading="lazy" />}
                      </span>
                      <span className="playlist-pick-body">
                        <span className="cell-strong">{m.title}</span>
                        <span className="cell-muted">{m.type} · {m.duration_sec}s</span>
                      </span>
                      <span className="playlist-pick-check">{idx >= 0 ? <span className="playlist-pick-order">{idx + 1}</span> : <Icon name="plus" />}</span>
                    </button>
                  );
                })}
              </div>
            )}
          </div>
        </Card>
      </div>
    );
  }

  return (
    <div className="dash">
      <div className="dash-head">
        <div>
          <div className="ds-kicker">Conteúdo</div>
          <h1>Playlists</h1>
          <p>Sequências de mídias atribuídas aos totens.</p>
        </div>
        <div className="row">
          <Button variant="outline" icon="refresh" onClick={load}>Atualizar</Button>
          <Button variant="brand" icon="plus" onClick={openNew}>Nova playlist</Button>
        </div>
      </div>

      {loading ? (
        <Card className="card-pad"><Skeleton h={120} /></Card>
      ) : playlists.length === 0 ? (
        <Card><EmptyState icon="list" title="Sem playlists" desc="Crie uma playlist para organizar suas mídias." action={<Button variant="outline" icon="plus" onClick={openNew}>Nova playlist</Button>} /></Card>
      ) : (
        <div className="playlist-grid">
          {playlists.map((pl) => (
            <Card key={pl.id} className="card-pad playlist-card">
              <div className="row" style={{ justifyContent: "space-between", alignItems: "flex-start" }}>
                <div className="row" style={{ gap: 12 }}>
                  <div className="sol-ic" style={{ background: "color-mix(in srgb, var(--brand) 16%, transparent)", color: "var(--brand)" }}><Icon name="list" /></div>
                  <div>
                    <div className="sol-name" style={{ fontSize: 16 }}>{pl.name}</div>
                    <div className="sol-meta">{pl.item_count != null ? `${pl.item_count} mídias` : "—"}</div>
                  </div>
                </div>
                <DropdownMenu align="right" trigger={<Button variant="ghost" size="sm" icon="more-horizontal" />}>
                  <MenuItem icon="edit" onClick={() => openEdit(pl)}>Editar</MenuItem>
                  <MenuSep />
                  <MenuItem icon="trash" danger onClick={() => remove(pl)}>Remover</MenuItem>
                </DropdownMenu>
              </div>
              <div className="card-footer" style={{ padding: "16px 0 0" }}>
                <Button variant="outline" size="sm" iconRight="arrowRight" onClick={() => openEdit(pl)}>Editar itens</Button>
              </div>
            </Card>
          ))}
        </div>
      )}
    </div>
  );
}

/* ============================================================
   CONFIGURAÇÕES — modelada na ConfigPage de referência + Sair
   ============================================================ */
function ConfigPage() {
  const t = useToast();
  const [me, setMe] = useStateTP(null);
  const [autoReload, setAutoReload] = useStateTP(true);
  const [notif, setNotif] = useStateTP(true);

  useEffectTP(() => {
    window.api.me().then((res) => setMe(res && res.user ? res.user : res)).catch(() => {});
  }, []);

  const logout = () => {
    window.api.logout().then(() => location.reload()).catch(() => location.reload());
  };

  return (
    <div className="dash" style={{ maxWidth: 760 }}>
      <div className="dash-head">
        <div>
          <div className="ds-kicker">Conta</div>
          <h1>Configurações</h1>
          <p>Gerencie sua conta e as preferências do workspace de totens.</p>
        </div>
      </div>

      <Card className="card-pad" style={{ marginBottom: 18 }}>
        <CardHeader title="Conta" desc="Seu usuário no Merion Totem" />
        <div className="card-body" style={{ padding: "16px 0 0" }}>
          <div className="row" style={{ gap: 14 }}>
            <Avatar size="lg" brand fallback={(me && me.name ? me.name : "M").slice(0, 1).toUpperCase()} />
            <div>
              <div className="cell-strong" style={{ fontSize: 16 }}>{me ? me.name : "—"}</div>
              <div className="cell-muted">{me ? me.email : ""}</div>
            </div>
            <Badge variant="soft" dot style={{ marginLeft: "auto" }}>{me && me.role ? me.role : "admin"}</Badge>
          </div>
        </div>
      </Card>

      <Card className="card-pad" style={{ marginBottom: 18 }}>
        <CardHeader title="Workspace" desc="Identidade da operação" />
        <div className="card-body stack-sm" style={{ gap: 16, padding: "16px 0 0" }}>
          <Field label="Nome do workspace"><Input defaultValue="Merion Totem" /></Field>
          <Field label="Fuso horário">
            <Select value="brt" onChange={() => {}} options={[{ value: "brt", label: "America/São_Paulo (BRT)" }, { value: "utc", label: "UTC" }]} />
          </Field>
        </div>
      </Card>

      <Card className="card-pad" style={{ marginBottom: 18 }}>
        <CardHeader title="Preferências" desc="Comportamento dos totens" />
        <div className="card-body stack-sm" style={{ gap: 4, padding: "8px 0 0" }}>
          <Switch checked={autoReload} onChange={setAutoReload} label="Recarregar totens ao salvar playlist" />
          <div className="card-sep" style={{ margin: "6px 0" }} />
          <Switch checked={notif} onChange={setNotif} label="Notificar quando um totem ficar offline" />
        </div>
      </Card>

      <Alert variant="destructive" title="Encerrar sessão">Você precisará entrar novamente com suas credenciais.</Alert>
      <div className="row" style={{ marginTop: 18 }}>
        <Button variant="brand" onClick={() => t({ title: "Preferências salvas", icon: "check-circle" })}>Salvar alterações</Button>
        <Button variant="outline" icon="logout" onClick={logout}>Sair</Button>
      </div>
    </div>
  );
}

Object.assign(window, {
  PainelPage, TotensPage, TotemDetalhe, NovoTotemSheet, MidiasPage, PlaylistsPage, ConfigPage,
});
