/* Devonew — Painel da Igreja · app shell */
const { useState: useStateApp, useEffect: useEffectApp, useRef: useRefApp } = React;
const A = window.ADMIN;

/* ---------- contexto multi-tenant ----------
   A igreja-demo (comunidadevida) mantém os dados de demonstração para servir
   de vitrine. QUALQUER outra igreja — recém-criada no onboarding — começa
   LIMPA, com o seu próprio nome (vindo de ?igreja= ou do subdomínio). */
(function setupTenant() {
  const tenant = (window.DEVO_TENANT || '').toLowerCase();
  if (!tenant || tenant === 'comunidadevida') return; // mantém os seeds da Comunidade Vida
  const q = new URLSearchParams(location.search);
  const ini = (s) => (s || '').trim().split(/\s+/).map((w) => w[0]).slice(0, 2).join('').toUpperCase();
  const titleCase = (s) => (s || '').replace(/-/g, ' ').replace(/\b\w/g, (c) => c.toUpperCase()).trim();
  const name = q.get('igreja') || (window.DEVO_CHURCH && window.DEVO_CHURCH.name) || titleCase(tenant);
  const adminName = q.get('gestor') || 'Administrador';
  // identidade da igreja
  A.church = {
    name, subdomain: tenant + '.devonew.com', plan: 'Gratuito',
    admin: { name: adminName, role: 'Administrador', initials: ini(adminName) || 'AD' },
    members: 0, address: '', instagram: '', whatsapp: '',
  };
  // zera os dados de demonstração (preservando a forma de cada estrutura)
  A.kpis = (A.kpis || []).map((k) => ({ ...k, value: '0', delta: '—' }));
  A.attendance = (A.attendance || []).map(() => 0);
  A.birthdays = []; A.activity = [];
  A.queue = []; A.members = []; A.cells = [];
  A.scale = { ...A.scale, grid: {}, conflict: null };
  A.finance = { monthIn: 0, monthOut: 0, entradas: [], saidas: [], recent: [] };
  A.events = []; A.courseSeed = []; A.fileSeed = [];
  A.devotionalsSeed = []; A.careSeed = []; A.visitorsSeed = [];
  // site público da nova igreja: já nasce com EXEMPLOS editáveis (horário e
  // texto) para nunca parecer vazio/quebrado. Endereço, telefone, pastores e
  // redes ficam em branco — são dados reais que o gestor preenche no painel
  // (e só então aparecem o mapa, a liderança e o WhatsApp no site).
  A.siteSeed = {
    name, logo: ini(name) || 'IG', logoImg: null, accent: { h: 145, c: 0.055 }, theme: 'classico', tagline: 'feito com devonew',
    hero: {
      kicker: 'Bem-vindo à ' + name,
      title: 'Um lugar de fé, família e recomeços.',
      subtitle: 'Seja a sua primeira vez ou o seu reencontro com Deus — aqui há lugar para você. Venha nos visitar.',
    },
    schedule: [
      { day: 'Domingo', time: '10h00', what: 'Culto de Celebração · toda a família' },
      { day: 'Quarta', time: '19h30', what: 'Estudo Bíblico e Oração' },
    ],
    location: { address: '', blurb: 'Você e a sua família são muito bem-vindos. Venha como está — há lugar para você aqui.', phone: '', maps: '' },
    about: {
      heading: 'No que cremos',
      text: 'Somos uma comunidade cristã que crê no amor de Deus, no poder transformador do evangelho e no valor de cada pessoa. Cremos que a fé se vive em comunidade — por isso caminhamos juntos, no cuidado mútuo.',
    },
    pastors: [], contact: { email: '', instagram: '', youtube: '', whatsapp: '' },
  };
  // mantidos: departments, journeySeed (template de discipulado), devoLibrary,
  // careRulesSeed/careHealthSeed (ilustrativos).
  // limpa a URL (?igreja=&gestor=) preservando ?screen= se houver
  if (q.get('novo') === '1') {
    const screen = q.get('screen');
    history.replaceState({}, '', location.pathname + (screen ? '?screen=' + screen : ''));
  }
})();

/* ---------- sincronização do conteúdo publicado (igrejas reais) ----------
   O que o admin publica para o site/app (site público, fotos, catálogo,
   biblioteca, eventos, trilha, devocionais) é espelhado no servidor via
   /api/church-state — assim visitantes e membros veem em QUALQUER aparelho,
   não só no navegador do admin. Demais chaves (secretaria, finanças,
   cuidado…) seguem locais até a próxima fatia da fase 3. */
const ADMIN_TENANT = (window.DEVO_TENANT || '').toLowerCase();
const ADMIN_IS_DEMO = !ADMIN_TENANT || ADMIN_TENANT === 'comunidadevida';
const CHURCH_PUB_KEYS = new Set(['site', 'catalog', 'library', 'events_pub', 'journey_def', 'devotionals_pub', 'groups_pub']);
const CHURCH_VALUE_MAX = 900000; // mesmo teto do servidor

let _churchSyncOn = false;
const _cSet = {};
const _cDel = new Set();
let _cFlushT = null;
let _cRetryMs = 5000;
function queueChurch(key, value) {
  if (!_churchSyncOn) return;
  _cDel.delete(key); _cSet[key] = value;
  clearTimeout(_cFlushT); _cFlushT = setTimeout(flushChurch, 800);
}
function queueChurchDel(key) {
  if (!_churchSyncOn) return;
  delete _cSet[key]; _cDel.add(key);
  clearTimeout(_cFlushT); _cFlushT = setTimeout(flushChurch, 800);
}
// valor grande demais p/ uma linha do banco: tira as capas (coverImg) dos
// itens e tenta de novo; se ainda assim passar do teto, não sobe a chave.
function fitChurchValue(v) {
  let raw = JSON.stringify(v === undefined ? null : v);
  if (raw.length <= CHURCH_VALUE_MAX) return v;
  if (Array.isArray(v)) {
    const slim = v.map((it) => (it && typeof it === 'object' && it.coverImg) ? { ...it, coverImg: null } : it);
    if (JSON.stringify(slim).length <= CHURCH_VALUE_MAX) return slim;
  }
  return undefined; // sinaliza "pula"
}
async function flushChurch() {
  const setKeys = Object.keys(_cSet);
  const delKeys = [..._cDel];
  if (!setKeys.length && !delKeys.length) return true;
  const set = {};
  for (const k of setKeys) {
    const v = fitChurchValue(_cSet[k]);
    delete _cSet[k];
    if (v === undefined) { console.warn('church-state: valor grande demais, não sincronizado:', k); continue; }
    set[k] = v;
  }
  delKeys.forEach((k) => _cDel.delete(k));
  try {
    const r = await fetch('/api/church-state', {
      method: 'PUT', headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ set, del: delKeys }), keepalive: true,
    });
    if (!r.ok) throw new Error('HTTP ' + r.status);
    _cRetryMs = 5000;
    return true;
  } catch (e) {
    Object.keys(set).forEach((k) => { if (!(k in _cSet)) _cSet[k] = set[k]; });
    delKeys.forEach((k) => _cDel.add(k));
    clearTimeout(_cFlushT); _cFlushT = setTimeout(flushChurch, _cRetryMs);
    _cRetryMs = Math.min(_cRetryMs * 2, 60000);
    return false;
  }
}
window.addEventListener('pagehide', () => flushChurch());
document.addEventListener('visibilitychange', () => { if (document.hidden) flushChurch(); });
// publica AGORA o que estiver pendente (botão "Salvar" do editor de site).
// Retorna {ok, pending}: ok=false → falhou (ficou na fila p/ retry).
window.DEVO_PUBLISH_NOW = async () => {
  clearTimeout(_cFlushT);
  const pending = Object.keys(_cSet).length + _cDel.size;
  const ok = await flushChurch();
  return { ok, pending };
};
// fotos do site (image-slot) — cada mudança vira uma chave imgslot:<id>
window.DEVO_SLOT_SYNC = (id, val) => {
  const key = 'imgslot:' + id;
  if (val) queueChurch(key, val); else queueChurchDel(key);
};

const ALS = {
  get(k, d) { try { const v = localStorage.getItem('devonew_' + k); return v == null ? d : JSON.parse(v); } catch { return d; } },
  set(k, v) {
    try { localStorage.setItem('devonew_' + k, JSON.stringify(v)); } catch {}
    if (CHURCH_PUB_KEYS.has(k)) queueChurch(k, v);
  },
  del(k) {
    try { localStorage.removeItem('devonew_' + k); } catch {}
    if (CHURCH_PUB_KEYS.has(k)) queueChurchDel(k);
  },
};
const rid = () => 'c' + Date.now().toString(36) + Math.random().toString(36).slice(2, 5);

const NAV = [
  { sec: 'Geral', items: [{ id: 'inicio', label: 'Início', icon: 'home' }] },
  { sec: 'Comunidade', items: [
    { id: 'secretaria', label: 'Secretaria', icon: 'userPlus', badge: 'queue' },
    { id: 'visitantes', label: 'Visitantes', icon: 'smile', badge: 'visit' },
    { id: 'cuidado', label: 'Cuidado', icon: 'heart', badge: 'care' },
    { id: 'testemunhos', label: 'Testemunhos', icon: 'quote', badge: 'testi' },
    { id: 'corrente', label: 'Corrente de Oração', icon: 'clock' },
    { id: 'devocional', label: 'Devocional', icon: 'book' },
    { id: 'trilha', label: 'Trilha de Crescimento', icon: 'sparkle' },
    { id: 'grupos', label: 'Pequenos Grupos', icon: 'church' },
    { id: 'voluntarios', label: 'Colaboradores', icon: 'users' },
  ]},
  { sec: 'Gestão', items: [
    { id: 'financas', label: 'Finanças', icon: 'wallet' },
    { id: 'eventos', label: 'Ensino & Eventos', icon: 'grad' },
  ]},
  { sec: 'Sistema', items: [
    { id: 'site', label: 'Site Público', icon: 'globe' },
    { id: 'config', label: 'Configurações', icon: 'settings' },
  ]},
];

const TITLES = {
  inicio: ['Início', 'Visão geral da sua igreja'],
  secretaria: ['Secretaria', 'Membros e fila de aprovação de cadastros'],
  visitantes: ['Visitantes', 'Quem chega pelo site — presença, oração, ajuda e contato'],
  cuidado: ['Cuidado', 'Acompanhamento pastoral, perfil 360 e saúde da igreja'],
  testemunhos: ['Testemunhos', 'Modere e publique o que Deus tem feito'],
  corrente: ['Corrente de Intercessão', 'Relógio de oração 24h — campanha e cobertura'],
  devocional: ['Devocional', 'Escreva, agende e publique o devocional diário do app'],
  trilha: ['Trilha de Crescimento', 'A jornada de discipulado dos seus membros'],
  grupos: ['Pequenos Grupos', 'Células e relatórios semanais'],
  voluntarios: ['Colaboradores', 'Departamentos e escalas de culto'],
  financas: ['Finanças', 'Fluxo de caixa e transparência'],
  eventos: ['Ensino & Eventos', 'Cursos, biblioteca e seminários online'],
  site: ['Site Público', 'Conteúdo da página pública da igreja'],
  config: ['Configurações da Igreja', 'Dados do templo e redes sociais'],
};

/* publish bridge → member app catalog (objeto completo: o membro matricula com conteúdo real) */
function bridgeCatalog(courses) {
  const totalMin = (c) => c.modules.flatMap((m) => m.lessons).reduce((s, l) => s + (parseInt(l.dur) || 0), 0);
  const fmt = (m) => m >= 60 ? `${Math.floor(m / 60)}h${String(m % 60).padStart(2, '0')}` : `${m} min`;
  const level = (n) => n <= 5 ? 'Iniciante' : n <= 9 ? 'Intermediário' : 'Avançado';
  const initials = (name) => (name || '').split(' ').filter(Boolean).map((w) => w[0]).slice(-2).join('').toUpperCase();
  return courses.filter((c) => c.status === 'publicado').map((c) => {
    const n = c.modules.reduce((s, m) => s + m.lessons.length, 0);
    return {
      id: c.id, title: c.title, instructor: c.instructor, instrInitials: c.instrInitials || initials(c.instructor),
      track: c.track || c.category, category: c.category, cover: c.cover, coverImg: c.coverImg || null,
      desc: c.desc || '', lessons: n, dur: fmt(totalMin(c)), level: level(n),
      modules: c.modules.map((m) => ({ name: m.name, lessons: m.lessons.map((l) => ({ ...l, done: false })) })),
    };
  });
}

function AdminApp() {
  const [screen, setScreen] = useStateApp(() => new URLSearchParams(location.search).get('screen') || 'inicio');
  const [queue, setQueue] = useStateApp(() => ALS.get('queue', A.queue));
  const [members, setMembers] = useStateApp(() => ALS.get('members', A.members));
  const [cells, setCells] = useStateApp(() => ALS.get('cells', A.cells));
  const [departments, setDepartments] = useStateApp(() => ALS.get('departments', A.departments || []));
  const [navOpen, setNavOpen] = useStateApp(false);
  const [finance, setFinance] = useStateApp(() => ALS.get('finance', A.finance));
  const [scale, setScale] = useStateApp(() => ALS.get('scale', A.scale));
  const [settings, setSettings] = useStateApp(() => ALS.get('church_settings', {
    name: A.church.name, subdomain: A.church.subdomain, plan: A.church.plan,
    address: A.church.address != null ? A.church.address : 'Av. Brasil, 1200 · Centro',
    instagram: A.church.instagram != null ? A.church.instagram : '@comunidadevida',
    whatsapp: A.church.whatsapp != null ? A.church.whatsapp : '(11) 3000-1200',
  }));
  const [courses, setCourses] = useStateApp(() => ALS.get('admin_courses', A.courseSeed));
  const [files, setFiles] = useStateApp(() => ALS.get('admin_files', A.fileSeed));
  const [events, setEvents] = useStateApp(() => ALS.get('admin_events', A.events));
  const [site, setSite] = useStateApp(() => ALS.get('site', A.siteSeed));
  const [testimonies, setTestimonies] = useStateApp(() => window.TESTI.load());
  const [campaign, setCampaign] = useStateApp(() => window.INTER.load());
  const [journey, setJourney] = useStateApp(() => ALS.get('admin_journey', A.journeySeed));
  const [devotionals, setDevotionals] = useStateApp(() => ALS.get('admin_devotionals', A.devotionalsSeed));
  const [care, setCare] = useStateApp(() => ALS.get('care', A.careSeed));
  const [visitors, setVisitors] = useStateApp(() => ALS.get('visitors', A.visitorsSeed || []));
  const [toastMsg, setToastMsg] = useStateApp(null);
  const [support, setSupport] = useStateApp(false);
  const toastTimer = useRefApp(null);
  const scrollRef = useRefApp(null);

  // persist + bridge published courses to member app
  useEffectApp(() => { ALS.set('admin_courses', courses); ALS.set('catalog', bridgeCatalog(courses)); }, [courses]);
  useEffectApp(() => { ALS.set('admin_files', files); ALS.set('library', files.filter((f) => f.status === 'publicado')); }, [files]);
  useEffectApp(() => { ALS.set('admin_events', events); ALS.set('events_pub', events.filter((e) => e.status === 'publicado')); }, [events]);
  useEffectApp(() => { ALS.set('site', site); }, [site]);
  // bridge: publica a trilha para o app do membro (despublicada → remove a chave, app volta ao padrão)
  useEffectApp(() => {
    ALS.set('admin_journey', journey);
    if (journey.status === 'publicada') ALS.set('journey_def', window.bridgeJourney(journey.steps));
    else ALS.del('journey_def');
  }, [journey]);
  // bridge: publica os devocionais (somente os publicados) para a aba Devocional do app
  useEffectApp(() => { ALS.set('admin_devotionals', devotionals); ALS.set('devotionals_pub', (devotionals || []).filter((d) => d.status === 'publicado')); }, [devotionals]);
  useEffectApp(() => { ALS.set('care', care); }, [care]);
  useEffectApp(() => { ALS.set('visitors', visitors); }, [visitors]);
  useEffectApp(() => { ALS.set('queue', queue); }, [queue]);
  useEffectApp(() => { ALS.set('members', members); }, [members]);
  // bridge: células publicadas no site público (só nome e dia — sem endereço do anfitrião)
  useEffectApp(() => {
    ALS.set('cells', cells);
    ALS.set('groups_pub', (cells || []).map((c) => ({ name: c.name, day: c.day })));
  }, [cells]);
  useEffectApp(() => { ALS.set('departments', departments); }, [departments]);
  useEffectApp(() => { ALS.set('finance', finance); }, [finance]);
  useEffectApp(() => { ALS.set('scale', scale); }, [scale]);
  useEffectApp(() => { ALS.set('church_settings', settings); }, [settings]);

  // cor de destaque da igreja aplicada ao próprio painel (feedback imediato)
  useEffectApp(() => {
    if (site && site.accent && site.accent.h != null) {
      const r = document.documentElement.style;
      r.setProperty('--accent-h', site.accent.h);
      r.setProperty('--accent-c', site.accent.c);
    }
  }, [site && site.accent && site.accent.h, site && site.accent && site.accent.c]);

  // pick up new member submissions / shift sign-ups when returning to the panel tab
  useEffectApp(() => {
    const reload = () => { setTestimonies(window.TESTI.load()); setCampaign(window.INTER.load()); setCare(ALS.get('care', A.careSeed)); if (ADMIN_IS_DEMO) setVisitors(ALS.get('visitors', A.visitorsSeed || [])); };
    const onVis = () => { if (!document.hidden) reload(); };
    window.addEventListener('focus', reload);
    document.addEventListener('visibilitychange', onVis);
    return () => { window.removeEventListener('focus', reload); document.removeEventListener('visibilitychange', onVis); };
  }, []);

  // igreja real: a Fila de aprovação vem dos cadastros PÚBLICOS pendentes no D1
  // (quem se inscreve pela página da igreja com e-mail+senha). Recarrega ao focar.
  useEffectApp(() => {
    if (ADMIN_IS_DEMO) return;
    let alive = true;
    const fmtWhen = (ts) => { try { return new Date(ts).toLocaleDateString('pt-BR'); } catch { return ''; } };
    const ini = (n) => (n || '').trim().split(/\s+/).map((w) => w[0]).slice(0, 2).join('').toUpperCase();
    const yearOf = (ts) => { const y = new Date(ts).getFullYear(); return String(y || new Date().getFullYear()); };
    const load = () => fetch('/api/members')
      .then((r) => (r.ok ? r.json() : null))
      .then((j) => {
        if (!alive || !j || !Array.isArray(j.members)) return;
        setQueue(j.members.filter((m) => m.status === 'pendente').map((m) => ({
          id: m.id, name: m.name, email: m.email, whats: '', via: 'Cadastro pelo site', when: fmtWhen(m.created_at), d1: true,
        })));
        // D1 → Membros: garante que os 'ativo' do banco apareçam na lista local
        // (gestor do onboarding, aprovados em outro aparelho…). Mescla por e-mail,
        // sem mexer nas fichas já existentes (que têm dados mais ricos).
        setMembers((ms) => {
          const have = new Set(ms.map((x) => (x.email || '').toLowerCase()).filter(Boolean));
          const add = j.members
            .filter((m) => m.status === 'ativo' && m.email && !have.has(m.email.toLowerCase()))
            .map((m) => ({ id: 'd1' + m.id, name: m.name, role: m.role === 'admin' ? 'Administrador' : 'Membro', cell: '—', since: yearOf(m.created_at), status: 'Ativo', initials: ini(m.name), email: m.email }));
          return add.length ? [...add, ...ms] : ms;
        });
      })
      .catch(() => {});
    load();
    const onVis = () => { if (!document.hidden) load(); };
    window.addEventListener('focus', load);
    document.addEventListener('visibilitychange', onVis);
    return () => { alive = false; window.removeEventListener('focus', load); document.removeEventListener('visibilitychange', onVis); };
  }, []);

  // igreja real: a lista de Visitantes vem do D1 (/api/visitors), preenchida
  // pelo formulário "Quero visitar" do site público. Recarrega ao focar.
  useEffectApp(() => {
    if (ADMIN_IS_DEMO) return;
    let alive = true;
    const load = () => fetch('/api/visitors')
      .then((r) => (r.ok ? r.json() : null))
      .then((j) => { if (alive && j && Array.isArray(j.visitors)) setVisitors(j.visitors); })
      .catch(() => {});
    load();
    const onVis = () => { if (!document.hidden) load(); };
    window.addEventListener('focus', load);
    document.addEventListener('visibilitychange', onVis);
    return () => { alive = false; window.removeEventListener('focus', load); document.removeEventListener('visibilitychange', onVis); };
  }, []);

  const toast = (m) => { setToastMsg(m); clearTimeout(toastTimer.current); toastTimer.current = setTimeout(() => setToastMsg(null), 2600); };
  const go = (s) => { setScreen(s); setNavOpen(false); if (scrollRef.current) scrollRef.current.scrollTop = 0; };

  const initialsOf = (name) => (name || '').trim().split(/\s+/).map((w) => w[0]).slice(0, 2).join('').toUpperCase();
  const addToRoster = (p) => setMembers((ms) => (
    (p.email && ms.some((x) => (x.email || '').toLowerCase() === p.email.toLowerCase()))
      ? ms
      : [{ id: rid(), name: p.name, role: 'Membro', cell: '—', since: String(new Date().getFullYear()), status: 'Ativo', initials: initialsOf(p.name), email: p.email, whats: p.whats || '' }, ...ms]
  ));
  const approve = (id) => {
    const p = queue.find((x) => x.id === id);
    if (!p) return;
    if (p.d1) { // cadastro público no D1 → aprova de verdade (status ativo)
      fetch('/api/members', { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ id: p.id, status: 'ativo' }) })
        .then((r) => { if (!r.ok) throw 0; addToRoster(p); setQueue((q) => q.filter((x) => x.id !== id)); toast('Cadastro aprovado · ' + p.name + ' já pode entrar no app ✓'); })
        .catch(() => toast('Não consegui aprovar agora — verifique a conexão'));
      return;
    }
    addToRoster(p); setQueue((q) => q.filter((x) => x.id !== id));
    toast('Cadastro aprovado · membro criado ✓');
  };
  const reject = (id) => {
    const p = queue.find((x) => x.id === id);
    if (p && p.d1) { // cadastro público no D1 → remove do banco
      fetch('/api/members', { method: 'DELETE', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ id: p.id }) })
        .then((r) => { if (!r.ok) throw 0; setQueue((q) => q.filter((x) => x.id !== id)); toast('Cadastro recusado e removido'); })
        .catch(() => toast('Não consegui recusar agora — verifique a conexão'));
      return;
    }
    setQueue((q) => q.filter((x) => x.id !== id));
    toast('Cadastro recusado');
  };

  /* ---- testemunhos: triagem de conteúdo ---- */
  const moderate = (id, fn) => setTestimonies(() => {
    const next = window.TESTI.load().map((t) => t.id === id ? fn(t) : t);
    window.TESTI.save(next); return next;
  });
  const approveTesti = (id) => { moderate(id, (t) => ({ ...t, status: 'aprovado' })); toast('Testemunho aprovado — já está abençoando a igreja ✓'); };
  const rejectTesti = (id) => { moderate(id, (t) => ({ ...t, status: 'recusado' })); toast('Testemunho recusado e arquivado'); };
  const editApproveTesti = (id, patch) => { moderate(id, (t) => ({ ...t, ...patch, status: 'aprovado' })); toast('Texto corrigido e aprovado ✓'); };
  const archiveTesti = (id) => { moderate(id, (t) => ({ ...t, status: 'recusado' })); toast('Testemunho retirado do mural'); };
  const restoreTesti = (id) => { moderate(id, (t) => ({ ...t, status: 'aprovado' })); toast('Testemunho restaurado e publicado ✓'); };

  /* ---- corrente de intercessão ---- */
  const persistCampaign = (next) => { window.INTER.save(next); setCampaign(next); };
  const changeCampaign = (next) => persistCampaign(next);
  const toggleCampaignStatus = () => {
    const c = window.INTER.load();
    const next = { ...c, status: c.status === 'publicada' ? 'rascunho' : 'publicada' };
    persistCampaign(next);
    toast(next.status === 'publicada' ? 'Campanha publicada — o módulo apareceu no app dos membros ✓' : 'Campanha despublicada — módulo oculto no app');
  };
  const createCampaign = () => { persistCampaign(window.INTER.newCampaign(A.church.admin.name)); toast('Campanha criada — configure e publique'); };
  const deleteCampaign = () => { window.INTER.clear(); setCampaign(null); toast('Campanha encerrada — módulo removido do app'); };
  const allocateShift = (day, idx, name) => {
    const c = window.INTER.load();
    if (window.INTER.slotFull(c, day, idx)) { toast('Esse turno já está completo'); return; }
    const initials = name.trim().split(/\s+/).map((w) => w[0]).slice(0, 2).join('').toUpperCase();
    persistCampaign({ ...c, reservations: [...(c.reservations || []), { day, idx, name: name.trim(), initials, anon: false, mine: false, doneAt: null }] });
  };
  const releaseShift = (day, idx) => {
    const c = window.INTER.load();
    persistCampaign({ ...c, reservations: (c.reservations || []).filter((r) => !(r.day === day && r.idx === idx)) });
    toast('Turno liberado');
  };

  /* ---- trilha de crescimento ---- */
  const resetJourney = () => setJourney(JSON.parse(JSON.stringify(A.journeySeed)));

  const newCourse = () => {
    const nid = rid();
    setCourses((cs) => [{ id: nid, title: 'Novo curso', instructor: A.church.admin.name, category: 'Geral',
      cover: 'capa · novo curso', coverImg: null, desc: '', status: 'rascunho', enrolled: 0, completion: 0,
      modules: [{ name: 'Módulo 1', lessons: [{ id: rid(), t: 'Primeira lição', dur: '8 min', type: 'video' }] }] }, ...cs]);
    return nid;
  };
  const deleteCourse = (id) => setCourses((cs) => cs.filter((c) => c.id !== id));

  const newFile = () => {
    const nid = 'fl' + rid();
    setFiles((fs) => [{ id: nid, title: 'Novo arquivo', desc: '', category: 'Geral', kind: 'link', url: '', mime: '', size: 0, status: 'rascunho', ts: Date.now() }, ...fs]);
    return nid;
  };
  const deleteFile = (id) => setFiles((fs) => fs.filter((f) => f.id !== id));

  const newEvent = () => {
    const nid = 'ev' + rid();
    setEvents((es) => [{ id: nid, name: 'Novo seminário', kind: 'Seminário', date: '', time: '', online: true, link: '', speaker: '', desc: '', signups: 0, cap: null, status: 'rascunho' }, ...es]);
    return nid;
  };
  const deleteEvent = (id) => setEvents((es) => es.filter((e) => e.id !== id));

  const [t1, t2] = TITLES[screen];
  const counts = { queue: queue.length, testi: testimonies.filter((t) => t.status === 'pendente').length,
    care: window.careRoster(care, members, cells).filter((p) => window.careNeedsCare(p)).length,
    visit: visitors.filter((v) => v.status === 'novo').length };
  const chName = (site && site.name) || settings.name || A.church.name;
  const chLogo = (site && site.logo) || chName.split(/\s+/).filter(Boolean).map((w) => w[0]).slice(0, 2).join('').toUpperCase();

  return (
    <div className={`admin ${navOpen ? 'nav-open' : ''}`}>
      {/* véu do menu lateral (celular) */}
      <div className="nav-ov" onClick={() => setNavOpen(false)}/>
      {/* sidebar */}
      <aside className="aside">
        <button className="aside-close" onClick={() => setNavOpen(false)} aria-label="Fechar menu"><Icon name="x" size={18}/></button>
        <div className="church-brand">
          {site && site.logoImg
            ? <span className="cb-logo has-img"><img src={site.logoImg} alt={chName}/></span>
            : <span className="cb-logo">{chLogo}</span>}
          <span className="cb-text">
            <span className="cb-name">{chName}</span>
            <span className="cb-sub">Painel da Igreja</span>
          </span>
        </div>
        <div className="aplan">
          <span className="ch-mark"><Icon name="globe" size={16}/></span>
          <div style={{ minWidth: 0 }}>
            <div className="ch-name">{settings.subdomain || A.church.subdomain}</div>
            <div className="ch-sub">Plano {settings.plan || A.church.plan}</div>
          </div>
          <span className="plan-tag">{settings.plan || A.church.plan}</span>
        </div>

        <nav className="anav">
          {NAV.map((g) => (
            <React.Fragment key={g.sec}>
              <div className="anav-label">{g.sec}</div>
              {g.items.map((n) => (
                <button key={n.id} className={`anav-item ${screen === n.id ? 'active' : ''}`} onClick={() => go(n.id)}>
                  <Icon name={n.icon} size={19}/>{n.label}
                  {n.badge && counts[n.badge] > 0 && <span className="count">{counts[n.badge]}</span>}
                </button>
              ))}
            </React.Fragment>
          ))}
        </nav>

        <div className="aside-foot">
          <button className="support-trigger" onClick={() => setSupport(true)} style={{ marginBottom: 12 }}>
            <Icon name="heart" size={17}/>Apoie o criador
          </button>
          <div className="aside-user">
            <div className="avatar sm">{A.church.admin.initials}</div>
            <div style={{ minWidth: 0, flex: 1 }}>
              <div className="nm">{A.church.admin.name}</div>
              <div className="rl">{A.church.admin.role}</div>
            </div>
            <button className="iconbtn" title="Sair" style={{ width: 34, height: 34 }}
              onClick={() => { const fin = () => { location.href = '/acesso'; }; fetch('/api/auth/logout', { method: 'POST' }).catch(() => {}).finally(fin); }}>
              <Icon name="logout" size={16}/></button>
          </div>
          <a className="powered" href={`//${window.DEVO_HOST || 'devonew.localhost:5050'}/`} title="Conheça o devonew">
            <span className="brand-mark sm"/><span>feito com <b>devonew</b></span>
          </a>
        </div>
      </aside>

      {/* main */}
      <div className="amain">
        <header className="atopbar">
          <button className="menu-toggle iconbtn" onClick={() => setNavOpen(true)} aria-label="Abrir menu">
            <Icon name="menu" size={20}/>
          </button>
          <div style={{ minWidth: 0 }}>
            <div className="atb-title">{t1}</div>
            <div className="atb-sub show-desktop">{t2}</div>
          </div>
          <div className="atb-spacer"/>
          <div className="searchbox"><Icon name="search" size={15}/><input placeholder="Buscar membros, células…"/></div>
          <button className="iconbtn" onClick={() => toast('Sem novas notificações')} aria-label="avisos"><Icon name="bell" size={18}/></button>
        </header>

        <div className="ascroll" ref={scrollRef}>
          {screen === 'inicio' && <DashboardScreen a={A} queue={queue} members={members} cells={cells} finance={finance} events={events} go={go}/>}
          {screen === 'secretaria' && <SecretariaScreen members={members} setMembers={setMembers} queue={queue} onApprove={approve} onReject={reject} departments={departments} setDepartments={setDepartments} toast={toast}/>}
          {screen === 'visitantes' && <VisitantesScreen visitors={visitors} setVisitors={setVisitors} isDemo={ADMIN_IS_DEMO} onSendToCare={(p) => setCare((rs) => (rs.some((x) => x.id === p.id) ? rs : [p, ...rs]))} toast={toast}/>}
          {screen === 'cuidado' && <CuidadoScreen care={care} setCare={setCare} members={members} cells={cells} journeySteps={journey.steps} toast={toast}/>}
          {screen === 'testemunhos' && <TestemunhosAdminScreen testimonies={testimonies} onApprove={approveTesti} onReject={rejectTesti} onEditApprove={editApproveTesti} onArchive={archiveTesti} onRestore={restoreTesti}/>}
          {screen === 'corrente' && <CorrenteAdminScreen campaign={campaign} onChange={changeCampaign} onToggleStatus={toggleCampaignStatus} onCreate={createCampaign} onDelete={deleteCampaign} onAllocate={allocateShift} onRelease={releaseShift} toast={toast}/>}
          {screen === 'devocional' && <DevocionalAdminScreen devotionals={devotionals} setDevotionals={setDevotionals} library={A.devoLibrary} toast={toast}/>}
          {screen === 'trilha' && <TrilhaAdminScreen journey={journey} setJourney={setJourney} resetJourney={resetJourney} courses={courses} memberCount={(A.church && A.church.members) || members.length} toast={toast}/>}
          {screen === 'grupos' && <GruposScreen cells={cells} setCells={setCells} toast={toast}/>}
          {screen === 'voluntarios' && <ColaboradoresScreen a={A} scale={scale} setScale={setScale} departments={departments} members={members} toast={toast} go={go}/>}
          {screen === 'financas' && <FinancasScreen finance={finance} setFinance={setFinance} toast={toast}/>}
          {screen === 'eventos' && <EventosCursosScreen a={A} courses={courses} setCourses={setCourses} files={files} setFiles={setFiles} events={events} setEvents={setEvents} toast={toast} newCourse={newCourse} deleteCourse={deleteCourse} newFile={newFile} deleteFile={deleteFile} newEvent={newEvent} deleteEvent={deleteEvent}/>}
          {screen === 'site' && <SitePublicoScreen site={site} setSite={setSite} toast={toast}/>}
          {screen === 'config' && <ConfigScreen settings={settings} setSettings={setSettings} setSite={setSite} toast={toast}/>}
        </div>
      </div>

      <div className={`toast ${toastMsg ? 'show' : ''}`}>{toastMsg && <><Icon name="check" size={17}/>{toastMsg}</>}</div>

      {support && <SupportModal onClose={() => setSupport(false)} toast={toast}
        sender={{ name: A.church.admin.name, role: A.church.admin.role, church: A.church.name, source: 'painel' }}/>}
    </div>
  );
}

/* ---------- boot ----------
   Igreja real: baixa o conteúdo publicado (church_state) para o cache local
   antes de renderizar — o servidor é a fonte; o que só existe neste
   aparelho (edições antigas) sobe na primeira sincronização. A demo
   renderiza direto, 100% local. */
const _adminRoot = ReactDOM.createRoot(document.getElementById('root'));

async function bootAdmin() {
  if (true) { // sempre sincroniza, mesmo para a demo
    try {
      const r = await fetch('/api/church-state');
      if (r.ok) {
        const st = (await r.json()).state || {};
        const imgs = {};
        for (const [k, v] of Object.entries(st)) {
          if (k.startsWith('imgslot:')) imgs[k.slice(8)] = v;
          else if (CHURCH_PUB_KEYS.has(k)) { try { localStorage.setItem('devonew_' + k, JSON.stringify(v)); } catch (e) {} }
        }
        const localSlots = (window.DEVO_IMGSLOTS_GET && window.DEVO_IMGSLOTS_GET()) || {};
        if (Object.keys(imgs).length && window.DEVO_IMGSLOTS_SET) window.DEVO_IMGSLOTS_SET(imgs);
        // primeira sincronização deste aparelho: sobe o que só existe aqui
        const up = {};
        for (const k of CHURCH_PUB_KEYS) {
          if (k in st) continue;
          const raw = localStorage.getItem('devonew_' + k);
          if (raw != null) { try { up[k] = JSON.parse(raw); } catch (e) {} }
        }
        for (const id of Object.keys(localSlots)) {
          if (!(('imgslot:' + id) in st)) up['imgslot:' + id] = localSlots[id];
        }
        _churchSyncOn = true;
        if (Object.keys(up).length) { Object.assign(_cSet, up); flushChurch(); }
      } else { _churchSyncOn = true; }
    } catch (e) { _churchSyncOn = true; } // offline → cache local; sobe quando voltar
  }
  _adminRoot.render(<AdminApp />);
}
bootAdmin();
