// App.jsx — Calendário multi-mês + Materiais por mês + Fotos
// Persistência: API Cloudflare (D1 + R2) via /api/*  (substitui o localStorage)
const { useReducer, useState, useEffect, useRef, useMemo } = React;

const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
  "paleta": ["#F5EDE0", "#FBF5E7", "#7A8B5C", "#C97B5A", "#A8C8D8", "#E8A95C"],
  "fontDisplay": "Fredoka"
}/*EDITMODE-END*/;

const PALETAS = {
  sol:    ['#F5EDE0', '#FBF5E7', '#7A8B5C', '#C97B5A', '#A8C8D8', '#E8A95C'],
  aurora: ['#f6e8db', '#fcf3e8', '#a16e8c', '#d68a6f', '#e4b07a', '#c4866d'],
  lagoa:  ['#e5e9d8', '#f5f3e3', '#5d8a8b', '#7e9b6a', '#a9c1c3', '#d4b67a'],
  outono: ['#f1ddc3', '#f8ecd4', '#8c5a3a', '#b66838', '#c89855', '#7d8a4f'],
};

const MES_NOMES = [
  'Janeiro', 'Fevereiro', 'Março', 'Abril', 'Maio', 'Junho',
  'Julho', 'Agosto', 'Setembro', 'Outubro', 'Novembro', 'Dezembro'
];
const DOW = ['Dom', 'Seg', 'Ter', 'Qua', 'Qui', 'Sex', 'Sáb'];
const EMOJIS_DIA = ['🌿', '🌸', '🐢', '⭐', '🍃', '🌼', '🐝', '🍯', '🌳', '🌻'];

const MIN_ANO = 2026;
const MIN_MES = 5; // 0-indexed: junho
const MAX_ANO = 2030;
const MAX_MES = 11;

// Mês atual segue em localStorage (preferência de UI, não dado da aluna)
const STORAGE_CURRENT = 'solu-mes-atual';
function loadCurrent() {
  try { const r = localStorage.getItem(STORAGE_CURRENT); return r ? JSON.parse(r) : null; }
  catch (_) { return null; }
}
function saveCurrent(v) {
  try { localStorage.setItem(STORAGE_CURRENT, JSON.stringify(v)); } catch (_) {}
}

// ============================================
// Camada de API (D1 + R2 via Pages Functions)
// ============================================
const api = {
  async getMes(ano, mes) {
    const r = await fetch(`/api/mes?ano=${ano}&mes=${mes}`);
    if (!r.ok) throw new Error('Falha ao carregar o mês');
    return r.json();
  },
  async salvarAula(ano, mes, dia, dados) {
    const body = {
      ano, mes, dia,
      assunto: dados.assunto || '',
      emoji: dados.emoji || '',
      materiais: dados.materiais || '',
      concluida: !!dados.concluida,
      atividades: (dados.atividades || []).map(a => ({ texto: a.texto, feito: !!a.feito })),
    };
    const r = await fetch('/api/aula', {
      method: 'PUT', headers: { 'content-type': 'application/json' }, body: JSON.stringify(body),
    });
    if (!r.ok) throw new Error('Falha ao salvar a aula');
    return r.json();
  },
  async apagarAula(ano, mes, dia) {
    const r = await fetch('/api/aula', {
      method: 'DELETE', headers: { 'content-type': 'application/json' },
      body: JSON.stringify({ ano, mes, dia }),
    });
    if (!r.ok) throw new Error('Falha ao apagar a aula');
    return r.json();
  },
  async setMatCheck(ano, mes, material, tenho) {
    const r = await fetch('/api/matcheck', {
      method: 'PUT', headers: { 'content-type': 'application/json' },
      body: JSON.stringify({ ano, mes, material, tenho }),
    });
    if (!r.ok) throw new Error('Falha ao salvar material');
    return r.json();
  },
  async uploadFoto(ano, mes, dia, foto) {
    const blob = await (await fetch(foto.src)).blob();
    const fd = new FormData();
    fd.append('file', blob, foto.nome || 'foto.jpg');
    fd.append('ano', String(ano));
    fd.append('mes', String(mes));
    fd.append('dia', String(dia));
    fd.append('legenda', foto.legenda || '');
    fd.append('nome', foto.nome || '');
    const r = await fetch('/api/fotos', { method: 'POST', body: fd });
    if (!r.ok) throw new Error('Falha ao enviar foto');
    return r.json();
  },
  async apagarFoto(id) {
    const r = await fetch(`/api/fotos/${id}`, { method: 'DELETE' });
    if (!r.ok) throw new Error('Falha ao remover foto');
    return r.json();
  },
  async editarLegendaFoto(id, legenda) {
    const r = await fetch(`/api/fotos/${id}`, {
      method: 'PATCH', headers: { 'content-type': 'application/json' },
      body: JSON.stringify({ legenda }),
    });
    if (!r.ok) throw new Error('Falha ao salvar legenda');
    return r.json();
  },
  // --- Auth própria (dormente atrás do Access até o cutover) ---
  async me() {
    const r = await fetch('/api/me');
    if (r.status === 401) return null; // sem sessão/identidade
    if (!r.ok) throw new Error('Falha ao verificar sessão');
    return r.json(); // { email, nome, role, convites_restantes }
  },
  async login(email, senha) {
    const r = await fetch('/api/auth/login', {
      method: 'POST', headers: { 'content-type': 'application/json' },
      body: JSON.stringify({ email, senha }),
    });
    const data = await r.json().catch(() => ({}));
    if (!r.ok) { const e = new Error(data.error || ''); e.min = data.min; throw e; } // data.error = CÓDIGO; AuthScreen traduz
    return data;
  },
  async cadastro(nome, email, senha, token) {
    const r = await fetch('/api/auth/cadastro', {
      method: 'POST', headers: { 'content-type': 'application/json' },
      body: JSON.stringify({ nome, email, senha, token }),
    });
    const data = await r.json().catch(() => ({}));
    if (!r.ok) { const e = new Error(data.error || ''); e.min = data.min; throw e; }
    return data;
  },
  async logout() {
    try { await fetch('/api/auth/logout', { method: 'POST' }); } catch (_) {}
  },
  async uploadFotoPerfil(file) {
    const fd = new FormData();
    fd.append('file', file, file.name || 'perfil.jpg');
    const r = await fetch('/api/me/foto', { method: 'POST', body: fd });
    if (!r.ok) throw new Error('Falha ao enviar a foto');
    return r.json(); // { fotoSrc }
  },
  async removerFotoPerfil() {
    const r = await fetch('/api/me/foto', { method: 'DELETE' });
    if (!r.ok) throw new Error('Falha ao remover a foto');
    return r.json();
  },
  async resetSolicitar(email) {
    // resposta sempre genérica (anti-enumeração) — não tratamos erro de "existe ou não"
    try {
      await fetch('/api/auth/reset-solicitar', {
        method: 'POST', headers: { 'content-type': 'application/json' },
        body: JSON.stringify({ email }),
      });
    } catch (_) {}
    return { ok: true };
  },
  async resetConfirmar(token, senha) {
    const r = await fetch('/api/auth/reset-confirmar', {
      method: 'POST', headers: { 'content-type': 'application/json' },
      body: JSON.stringify({ token, senha }),
    });
    const data = await r.json().catch(() => ({}));
    if (!r.ok) throw new Error(data.error || 'Não consegui redefinir a senha.');
    return data;
  },
  async listarConvites() {
    const r = await fetch('/api/convites');
    if (!r.ok) throw new Error('Falha ao carregar convites');
    return r.json(); // { convites: [...] }
  },
  async gerarConvite() {
    const r = await fetch('/api/convites', { method: 'POST' });
    const data = await r.json().catch(() => ({}));
    if (!r.ok) throw new Error(data.error || 'Não consegui gerar o convite.');
    return data; // { ok, token, expira_em }
  },
  // --- Filhos ---
  async getFilhos() {
    const r = await fetch('/api/filhos');
    if (!r.ok) throw new Error('Falha ao carregar filhos');
    return r.json(); // { filhos: [...] }
  },
  async salvarFilho(filho) {
    const r = await fetch('/api/filhos', {
      method: 'PUT', headers: { 'content-type': 'application/json' },
      body: JSON.stringify(filho),
    });
    if (!r.ok) throw new Error('Falha ao salvar filho');
    return r.json();
  },
  async apagarFilho(id) {
    const r = await fetch('/api/filhos', {
      method: 'DELETE', headers: { 'content-type': 'application/json' },
      body: JSON.stringify({ id }),
    });
    if (!r.ok) throw new Error('Falha ao remover filho');
    return r.json();
  },
  async uploadFotoFilho(id, file) {
    const fd = new FormData();
    fd.append('file', file, file.name || 'foto.jpg');
    const r = await fetch(`/api/filhos/foto/${id}`, { method: 'POST', body: fd });
    if (!r.ok) throw new Error('Falha ao enviar foto do filho');
    return r.json(); // { fotoSrc }
  },
};
window.SoluApi = api;

function getMonthMeta(year, month) {
  const firstDay   = new Date(year, month, 1).getDay();
  const daysInMonth = new Date(year, month + 1, 0).getDate();
  return { firstDay, daysInMonth };
}
function ehAntesDoMinimo(year, month) {
  return year < MIN_ANO || (year === MIN_ANO && month < MIN_MES);
}
function ehDepoisDoMaximo(year, month) {
  return year > MAX_ANO || (year === MAX_ANO && month > MAX_MES);
}

// Mês de abertura: o lembrado no dispositivo; sem ele, o mês corrente (clampado ao range)
function mesInicial() {
  const s = loadCurrent();
  if (s && s.ano && typeof s.mes === 'number') return s;
  const hoje = new Date();
  let ano = hoje.getFullYear(), mes = hoje.getMonth();
  if (ehAntesDoMinimo(ano, mes)) { ano = MIN_ANO; mes = MIN_MES; }
  if (ehDepoisDoMaximo(ano, mes)) { ano = MAX_ANO; mes = MAX_MES; }
  return { ano, mes };
}

function SoluLogo({ className = '' }) {
  return (<div className={`solu-logo ${className}`} aria-label="Solukids">solukids</div>);
}

// Ícones próprios do nav — minimalistas, traço fino, herdam a cor via currentColor
function NavIcone({ nome }) {
  const p = { viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', strokeWidth: 1.2, strokeLinecap: 'round', strokeLinejoin: 'round' };
  if (nome === 'inicio') return (
    <svg {...p}><path d="M4 10.8 12 4.5l8 6.3" /><path d="M6.4 9.7V19h11.2V9.7" /></svg>
  );
  if (nome === 'calendario') return (
    <svg {...p}><rect x="4" y="5.5" width="16" height="14.5" rx="3.5" /><path d="M4 9.5h16" /><path d="M8.2 3.6v2.6M15.8 3.6v2.6" /></svg>
  );
  if (nome === 'materiais') return (
    <svg {...p}><path d="M4.6 9h14.8l-1.1 9c-.1.7-.7 1.2-1.4 1.2H7.1c-.7 0-1.3-.5-1.4-1.2L4.6 9Z" /><path d="M3.2 9h17.6" /></svg>
  );
  // memorias
  return (
    <svg {...p}><rect x="4" y="6" width="16" height="13" rx="3.5" /><circle cx="8.8" cy="10.8" r="1.3" /><path d="M4.6 16.6 9 12.6l3 2.2 3-2.6 4.4 4.1" /></svg>
  );
}
window.SoluLogo = SoluLogo;

function App() {
  const [t, setTweak] = useTweaks(TWEAK_DEFAULTS);

  // Portão de auth própria. undefined = verificando, null = não logada, {…} = logada.
  // Atrás do Access, /api/me responde 200 (via header) → o app aparece e a AuthScreen
  // fica dormente. Ela só "acende" no cutover, quando /api/me passa a poder dar 401.
  const [auth, setAuth] = useState(undefined);
  useEffect(() => {
    let vivo = true;
    api.me().then(u => { if (vivo) setAuth(u || null); })
            .catch(() => { if (vivo) setAuth(null); });
    return () => { vivo = false; };
  }, []);

  // Some com o splash estático (do HTML) assim que o auth resolve — ele respeita o piso de 1,8s
  useEffect(() => {
    if (auth !== undefined) { try { window.__hideSplash && window.__hideSplash(); } catch (_) {} }
  }, [auth]);

  const [ano, setAno] = useState(() => mesInicial().ano);
  const [mes, setMes] = useState(() => mesInicial().mes);

  const [anotacoes, setAnotacoes] = useState({});
  const [matCheck, setMatCheck]   = useState({});
  const [carregando, setCarregando] = useState(true);
  // Fins de semana no calendário: só da sessão (sem localStorage) — reiniciou, volta a seg–sex
  const [mostrarFds, setMostrarFds] = useState(false);
  // Onboarding: só pra conta nova (sem filhos e sem aulas); marcado como visto pra sempre via localStorage
  const [onboardingOk, setOnboardingOk] = useState(() => {
    try { return localStorage.getItem('solu-onboarding-ok') === '1'; } catch (_) { return true; }
  });
  const [erro, setErro] = useState(null);

  const [tela, setTela] = useState('inicio');
  const [diaAberto, setDiaAberto] = useState(null);
  const [criandoNovo, setCriandoNovo] = useState(false); // modal aberto via FAB "+": modo criar-com-data
  const [idioma, setIdioma] = useState(() => (window.SoluI18n ? window.SoluI18n.idioma : 'pt'));
  const trocarIdioma = (lang) => { if (window.SoluI18n) window.SoluI18n.set(lang); setIdioma(lang); };
  const [fotoDia, setFotoDia] = useState(null);
  const [celebrando, setCelebrando] = useState(false);

  // Filhos da conta (carregados do banco)
  const [filhos, setFilhos] = useState([]);
  const [gerenciarFilhos, setGerenciarFilhos] = useState(false);
  const [contaAberta, setContaAberta] = useState(false);

  const sair = async () => {
    await api.logout();
    setContaAberta(false);
    setAuth(null);
  };

  const [filhosCarregados, setFilhosCarregados] = useState(false);
  const carregarFilhos = React.useCallback(() => {
    api.getFilhos()
      .then(({ filhos }) => setFilhos(filhos || []))
      .catch(() => {/* silencioso: header só fica sem pills */})
      .finally(() => setFilhosCarregados(true));
  }, []);

  useEffect(() => { if (auth) carregarFilhos(); }, [carregarFilhos, auth]);

  // Celebração leve no dia do aniversário (uma vez por dia, por filho)
  useEffect(() => {
    if (!filhos.length || !window.SoluIdade) return;
    const hojeKey = new Date().toISOString().slice(0, 10);
    const aniversariantes = filhos.filter(f => {
      if (!f.nascimento) return false;
      const s = window.SoluIdade.statusAniversario(f.nascimento, f.nome);
      return s && s.tipo === 'hoje';
    });
    if (!aniversariantes.length) return;
    const marca = `solu-aniv-celebrado-${hojeKey}`;
    let jaCelebrados = [];
    try { jaCelebrados = JSON.parse(localStorage.getItem(marca) || '[]'); } catch (_) {}
    const novos = aniversariantes.filter(f => !jaCelebrados.includes(f.id));
    if (novos.length) {
      setCelebrando(true);
      try { localStorage.setItem(marca, JSON.stringify([...jaCelebrados, ...novos.map(f => f.id)])); } catch (_) {}
    }
  }, [filhos]);

  useEffect(() => {
    if (!auth) return; // só carrega dados depois de autenticada
    let cancelado = false;
    setCarregando(true); setErro(null);
    saveCurrent({ ano, mes });
    setDiaAberto(null); setFotoDia(null);
    api.getMes(ano, mes)
      .then(({ anotacoes, matCheck }) => {
        if (cancelado) return;
        setAnotacoes(anotacoes || {});
        setMatCheck(matCheck || {});
      })
      .catch(e => { if (!cancelado) setErro(e.message || 'Erro ao carregar'); })
      .finally(() => { if (!cancelado) setCarregando(false); });
    return () => { cancelado = true; };
  }, [ano, mes, auth]);

  useEffect(() => {
    const r = document.documentElement;
    const p = t.paleta || PALETAS.sol;
    r.style.setProperty('--bg',    p[0]);
    r.style.setProperty('--paper', p[1]);
    r.style.setProperty('--musgo', p[2]);
    r.style.setProperty('--terra', p[3]);
    r.style.setProperty('--ceu',   p[4]);
    r.style.setProperty('--mel',   p[5]);
    r.style.setProperty('--solu-cream',      p[0]);
    r.style.setProperty('--solu-moss',       p[2]);
    r.style.setProperty('--solu-terracotta', p[3]);
    r.style.setProperty('--solu-sky',        p[4]);
    r.style.setProperty('--solu-honey',      p[5]);
    r.style.setProperty('--display', `'${t.fontDisplay}', 'Nunito', system-ui, sans-serif`);
  }, [t.paleta, t.fontDisplay]);

  const meta = getMonthMeta(ano, mes);
  const nomeMes = window.SoluI18n ? window.SoluI18n.nomeMes(ano, mes) : `${MES_NOMES[mes]} ${ano}`;
  const podeVoltar  = !ehAntesDoMinimo(ano, mes === 0 ? mes : mes - 1) && !(ano === MIN_ANO && mes === MIN_MES);
  const podeAvancar = !ehDepoisDoMaximo(ano, mes === 11 ? mes : mes + 1) && !(ano === MAX_ANO && mes === MAX_MES);

  const irMesAnterior = () => {
    if (!podeVoltar) return;
    if (mes === 0) { setAno(ano - 1); setMes(11); } else { setMes(mes - 1); }
  };
  const irMesProximo = () => {
    if (!podeAvancar) return;
    if (mes === 11) { setAno(ano + 1); setMes(0); } else { setMes(mes + 1); }
  };

  // Atalho "+ Criar" (FAB central do rodapé): abre o modal de NOVA aula (com seletor de data), default HOJE.
  // Pula pro mês atual se estiver vendo outro; se hoje cair fora do range, abre no início do range.
  const criarAulaHoje = () => {
    const hoje = new Date();
    let y = hoje.getFullYear(), m = hoje.getMonth(), d = hoje.getDate();
    if (ehAntesDoMinimo(y, m)) { y = MIN_ANO; m = MIN_MES; d = 1; }
    if (ehDepoisDoMaximo(y, m)) { y = MAX_ANO; m = MAX_MES; d = 1; }
    setTela('calendario');
    setCriandoNovo(true);
    if (y !== ano || m !== mes) { setAno(y); setMes(m); }
    setTimeout(() => setDiaAberto(d), 80);
  };

  // Re-aponta o modal de criação pra outra data (muda o mês visível se preciso). Usado pelo seletor de data.
  const irParaData = (dataISO) => {
    const [a, m, d] = (dataISO || '').split('-').map(Number);
    if (!a || !m || !d) return;
    const mesAlvo = m - 1; // ISO é 1-indexado; o app, 0-indexado
    if (ehAntesDoMinimo(a, mesAlvo) || ehDepoisDoMaximo(a, mesAlvo)) return;
    if (a !== ano) setAno(a);
    if (mesAlvo !== mes) setMes(mesAlvo);
    setDiaAberto(d);
  };

  const salvar = async (dia, dados, opts = {}) => {
    const backup = anotacoes[dia]; // undefined se o dia estava vazio
    const atual = anotacoes[dia] || {};
    const merged = { ...atual, ...dados };
    setAnotacoes(prev => ({ ...prev, [dia]: merged }));
    if (opts.celebrar) {
      setCelebrando(true);
      setDiaAberto(null);
      setTimeout(() => setFotoDia(dia), 700);
    }
    try { await api.salvarAula(ano, mes, dia, merged); }
    catch (e) {
      setErro(window.t('erro.salvar'));
      // Reverte o otimismo — mas só se nenhuma edição mais nova sobrescreveu este dia
      setAnotacoes(prev => {
        if (prev[dia] !== merged) return prev;
        const n = { ...prev };
        if (backup === undefined) delete n[dia]; else n[dia] = backup;
        return n;
      });
    }
  };

  const apagar = async (dia) => {
    const backup = anotacoes[dia];
    setAnotacoes(prev => { const n = { ...prev }; delete n[dia]; return n; });
    setDiaAberto(null);
    try { await api.apagarAula(ano, mes, dia); }
    catch (e) { setErro(window.t('erro.apagar')); setAnotacoes(prev => ({ ...prev, [dia]: backup })); }
  };

  const adicionarFotos = async (dia, novasFotos) => {
    for (const foto of novasFotos) {
      try {
        const salva = await api.uploadFoto(ano, mes, dia, foto);
        setAnotacoes(prev => {
          const atual = prev[dia] || { assunto: '', atividades: [], materiais: '', fotos: [] };
          const fotos = [...(atual.fotos || []), salva];
          return { ...prev, [dia]: { ...atual, fotos } };
        });
      } catch (e) { setErro(window.t('erro.fotoUm')); }
    }
  };

  const removerFoto = async (dia, idx) => {
    const atual = anotacoes[dia];
    if (!atual) return;
    const foto = (atual.fotos || [])[idx];
    setAnotacoes(prev => {
      const a = prev[dia]; if (!a) return prev;
      const fotos = (a.fotos || []).filter((_, i) => i !== idx);
      return { ...prev, [dia]: { ...a, fotos } };
    });
    if (foto && foto.id) {
      try { await api.apagarFoto(foto.id); } catch (e) { setErro(window.t('erro.foto')); }
    }
  };

  const editarLegenda = async (dia, idx, legenda) => {
    const atual = anotacoes[dia];
    if (!atual) return;
    const foto = (atual.fotos || [])[idx];
    if (!foto) return;
    setAnotacoes(prev => {
      const a = prev[dia]; if (!a) return prev;
      const fotos = (a.fotos || []).map((f, i) => i === idx ? { ...f, legenda } : f);
      return { ...prev, [dia]: { ...a, fotos } };
    });
    if (foto.id) {
      try { await api.editarLegendaFoto(foto.id, legenda); }
      catch (e) { setErro(window.t('erro.legenda')); }
    }
  };

  // Duplica a aula de um dia para outra data (pode ser outro mês), sem fotos e com checkboxes zerados
  const duplicarAula = async (diaOrigem, dataISO) => {
    const origem = anotacoes[diaOrigem];
    if (!origem) return { ok: false, msg: window.t('dup.naoEncontrada') };
    const [a, m, d] = (dataISO || '').split('-').map(Number);
    if (!a || !m || !d) return { ok: false, msg: window.t('dup.dataInvalida') };
    const mesDestino = m - 1; // ISO usa mês 1-indexado; o app, 0-indexado
    if (ehAntesDoMinimo(a, mesDestino) || ehDepoisDoMaximo(a, mesDestino)) {
      return { ok: false, msg: window.t('dup.foraPeriodo') };
    }
    if (a === ano && mesDestino === mes && d === diaOrigem) {
      return { ok: false, msg: window.t('dup.mesmoDia') };
    }
    // Fim de semana só aparece com "Mostrar fins de semana" marcado — confirmar pra não "sumir" aula
    const diaSemanaDestino = new Date(a, mesDestino, d).getDay();
    if (diaSemanaDestino === 0 || diaSemanaDestino === 6) {
      if (!confirm(window.t('dup.fdsConfirm'))) {
        return { ok: false, msg: null };
      }
    }

    // Nunca sobrescrever em silêncio: busca o mês de destino FRESCO do banco
    // (o estado local pode estar velho — PWA suspenso, segundo dispositivo)
    let existente = null;
    try {
      const mesDest = await api.getMes(a, mesDestino);
      existente = ((mesDest && mesDest.anotacoes) || {})[d] || null;
    } catch (e) {
      return { ok: false, msg: window.t('dup.verificarErro') };
    }
    if (existente) {
      const rotulo = existente.assunto || window.t('dia.semTitulo');
      const dataFmt = `${String(d).padStart(2, '0')}/${String(m).padStart(2, '0')}`;
      if (!confirm(window.t('dup.ocupadoConfirm', { data: dataFmt, rotulo }))) {
        return { ok: false, msg: null }; // cancelado pela usuária
      }
    }

    const copia = {
      assunto: origem.assunto || '',
      emoji: origem.emoji || '',
      materiais: origem.materiais || '',
      concluida: false,
      atividades: (origem.atividades || []).map(at => ({ texto: at.texto, feito: false })),
    };

    try { await api.salvarAula(a, mesDestino, d, copia); }
    catch (e) { return { ok: false, msg: window.t('dia.duplicarErro') }; }

    // Destino no mês visível: reflete na tela (já persistido — não é otimismo).
    // Ao substituir, o aula_id se mantém e as fotos do destino sobrevivem no banco.
    if (a === ano && mesDestino === mes) {
      setAnotacoes(prev => ({ ...prev, [d]: { ...copia, fotos: (existente && existente.fotos) || [] } }));
    }
    return { ok: true, msg: window.t('dup.sucesso', { data: `${String(d).padStart(2, '0')}/${String(m).padStart(2, '0')}/${a}` }) };
  };

  const toggleMat = async (mat) => {
    const novo = !matCheck[mat];
    setMatCheck(prev => ({ ...prev, [mat]: novo }));
    try { await api.setMatCheck(ano, mes, mat, novo); }
    catch (e) { setErro('Não consegui salvar o material.'); }
  };

  // Posts do feed de Memórias: dias do mês com foto, do mais recente pro mais antigo
  const postsMemorias = Object.entries(anotacoes)
    .filter(([, d]) => (d.fotos || []).length > 0)
    .map(([dia, d]) => ({ ano, mes, dia: parseInt(dia, 10), assunto: d.assunto, emoji: d.emoji, fotos: d.fotos }))
    .sort((a, b) => b.dia - a.dia);

  const totalDias = Object.keys(anotacoes).length;
  const totalFotos = Object.values(anotacoes).reduce((acc, d) => acc + ((d.fotos || []).length), 0);

  // Conta em uso (tem filhos E aulas) nunca mais vê o onboarding
  useEffect(() => {
    if (!onboardingOk && filhos.length > 0 && totalDias > 0) {
      setOnboardingOk(true);
      try { localStorage.setItem('solu-onboarding-ok', '1'); } catch (_) {}
    }
  }, [onboardingOk, filhos.length, totalDias]);

  const dispensarOnboarding = () => {
    setOnboardingOk(true);
    try { localStorage.setItem('solu-onboarding-ok', '1'); } catch (_) {}
  };
  // filhosCarregados evita o card piscar enquanto o fetch dos filhos não voltou
  const mostrarBoasVindas = filhosCarregados && !carregando && !onboardingOk && filhos.length === 0 && totalDias === 0;

  // Portão de auth: enquanto verifica, o splash estático do HTML cobre a tela (retornamos null).
  if (auth === undefined) return null;
  if (auth === null) {
    return <AuthScreen onAutenticado={(u) => setAuth(u)} idioma={idioma} onTrocarIdioma={trocarIdioma} />;
  }

  return (
    <div className="app">
      <header className="header">
        <div className="header-title">
          <div className="kicker-row">
            <span className="kicker">{window.t('tagline')}</span>
            {auth && (
              <button className="conta-btn" onClick={() => setContaAberta(true)}
                title={window.t('header.minhaConta')} aria-label={window.t('header.minhaConta')}>
                {auth.foto
                  ? <img className="conta-btn-foto" src={auth.foto} alt="" />
                  : (auth.nome || auth.email || '?').charAt(0).toUpperCase()}
              </button>
            )}
          </div>
          <SoluLogo />
          <div className="filhas-strip">
            {filhos.map(f => {
              const idadeCalc = f.nascimento && window.SoluIdade
                ? window.SoluIdade.calcularIdade(f.nascimento)
                : null;
              const idadeTxt = idadeCalc ? idadeCalc.texto : (f.idade || '');
              const aniv = f.nascimento && window.SoluIdade
                ? window.SoluIdade.statusAniversario(f.nascimento, f.nome)
                : null;
              const ehAniversario = aniv && aniv.tipo === 'hoje';
              return (
                <div className={`filha-pill ${ehAniversario ? 'filha-pill-aniv' : ''}`} key={f.id}>
                  {f.fotoSrc
                    ? <img className="filha-avatar" src={f.fotoSrc} alt={f.nome} />
                    : <div className="filha-avatar filha-avatar-vazio" aria-hidden="true">{(f.nome || '?').charAt(0).toUpperCase()}</div>}
                  <div className="filha-pill-info">
                    <span className="filha-pill-nome">
                      {f.nome}
                      {ehAniversario && <span className="filha-aniv-cake" aria-label="aniversário">🎂</span>}
                    </span>
                    {idadeTxt ? <span className="filha-pill-idade">{idadeTxt}</span> : null}
                    {aniv && (
                      <span className={`filha-aniv-msg filha-aniv-${aniv.tipo}`}>{aniv.mensagem}</span>
                    )}
                  </div>
                </div>
              );
            })}
            <button className="filha-add" onClick={() => setGerenciarFilhos(true)}
              title={filhos.length ? window.t('header.maisFilhos') : window.t('header.adicionarFilho')}>
              {filhos.length ? window.t('header.maisFilhos') : window.t('header.adicionarFilho')}
            </button>
          </div>
        </div>
        <div className="header-mes">
          <div className="mes-nav">
            <button className="mes-arrow" onClick={irMesAnterior} disabled={!podeVoltar}
              aria-label={window.t('header.mesAnterior')} title={podeVoltar ? window.t('header.mesAnterior') : window.t('header.inicioPeriodo')}>‹</button>
            <div className="mes-label">
              <span className="grande">{nomeMes}</span>
              <span style={{ fontSize: 13, fontFamily: 'var(--corpo)', fontStyle: 'italic', color: 'var(--ink-soft)' }}>
                {carregando ? window.t('header.carregando') : (totalDias === 0
                  ? window.t('header.nenhumaAula')
                  : window.t('header.resumo', { nAulas: totalDias, nFotos: totalFotos }))}
              </span>
            </div>
            <button className="mes-arrow" onClick={irMesProximo} disabled={!podeAvancar}
              aria-label={window.t('header.proximoMes')} title={podeAvancar ? window.t('header.proximoMes') : ''}>›</button>
          </div>
        </div>
      </header>

      {erro && (
        <div className="erro-banner" onClick={() => setErro(null)} style={{
          background: '#fbeae6', color: '#9a3b25', padding: '10px 16px', borderRadius: 10,
          margin: '0 auto 12px', maxWidth: 600, textAlign: 'center', cursor: 'pointer', fontSize: 14 }}>
          {erro} <span style={{ opacity: .6 }}>{window.t('header.fecharErro')}</span>
        </div>
      )}

      <nav className="nav">
        {/* Pílula que escorrega entre as abas (só mobile) — pula a coluna do meio (FAB Criar) */}
        <span className="nav-pilula" aria-hidden="true"
          style={{ '--i': { inicio: 0, calendario: 1, materiais: 3, memorias: 4 }[tela] || 0 }} />
        <button className={tela === 'inicio' ? 'active' : ''} onClick={() => setTela('inicio')}>
          <span className="nav-ic"><NavIcone nome="inicio" /></span> {window.t('nav.inicio')}
        </button>
        <button className={tela === 'calendario' ? 'active' : ''} onClick={() => setTela('calendario')}>
          <span className="nav-ic"><NavIcone nome="calendario" /></span> {window.t('nav.calendario')}
        </button>
        {/* FAB central: atalho pra criar a aula de hoje (mobile-only via CSS) */}
        <button className="nav-criar" onClick={criarAulaHoje}
          aria-label={window.t('nav.criarAula')} title={window.t('nav.criarAula')}>
          <span className="nav-criar-ic" aria-hidden="true">+</span>
        </button>
        <button className={tela === 'materiais' ? 'active' : ''} onClick={() => setTela('materiais')}>
          <span className="nav-ic"><NavIcone nome="materiais" /></span> {window.t('nav.materiais')}<span className="nav-do-mes">{window.t('nav.doMes')}</span>
        </button>
        <button className={tela === 'memorias' ? 'active' : ''} onClick={() => setTela('memorias')}>
          <span className="nav-ic"><NavIcone nome="memorias" /></span> {window.t('nav.memorias')}
        </button>
      </nav>

      {tela === 'inicio' && (
        <HomeScreen auth={auth} filhos={filhos} anotacoes={anotacoes} ano={ano} mes={mes} nomeMes={nomeMes}
          onAbrirDia={(dia) => { setTela('calendario'); setTimeout(() => setDiaAberto(dia), 80); }}
          onIrTela={setTela} />
      )}
      {tela === 'calendario' && (
        <>
          {mostrarBoasVindas && (
            <BoasVindas onAdicionarFilhos={() => setGerenciarFilhos(true)} onDispensar={dispensarOnboarding} />
          )}
          <Calendario anotacoes={anotacoes} meta={meta} nomeMes={nomeMes} onAbrirDia={(dia) => setDiaAberto(dia)}
            mostrarFds={mostrarFds} onToggleFds={setMostrarFds} />
        </>
      )}
      {tela === 'materiais' && (
        <MateriaisMes anotacoes={anotacoes} matCheck={matCheck} nomeMes={nomeMes}
          onToggleMat={toggleMat}
          onIrParaDia={(dia) => { setTela('calendario'); setTimeout(() => setDiaAberto(dia), 80); }} />
      )}
      {tela === 'memorias' && (
        <FeedMemorias posts={postsMemorias}
          onAbrirDia={(dia) => { setTela('calendario'); setTimeout(() => setDiaAberto(dia), 80); }} />
      )}

      {diaAberto !== null && (
        <DiaModal dia={diaAberto} nomeMes={nomeMes} ano={ano} mes={mes} dados={anotacoes[diaAberto]}
          criando={criandoNovo} onMudarData={irParaData}
          onSalvar={(dados, opts) => salvar(diaAberto, dados, opts)}
          onApagar={() => apagar(diaAberto)}
          onAbrirFotos={() => { setFotoDia(diaAberto); setDiaAberto(null); }}
          onRemoverFoto={(idx) => removerFoto(diaAberto, idx)}
          onDuplicar={(dataISO) => duplicarAula(diaAberto, dataISO)}
          onClose={() => { setDiaAberto(null); setCriandoNovo(false); }} />
      )}

      {fotoDia !== null && (
        <FotoModal dia={fotoDia} nomeMes={nomeMes} dados={anotacoes[fotoDia]}
          onAdicionar={(fs) => adicionarFotos(fotoDia, fs)}
          onRemover={(idx) => removerFoto(fotoDia, idx)}
          onEditarLegenda={(idx, legenda) => editarLegenda(fotoDia, idx, legenda)}
          onClose={() => setFotoDia(null)} />
      )}

      <Celebracao ativo={celebrando} onDone={() => setCelebrando(false)} />
      <TweaksAppPanel t={t} setTweak={setTweak} ano={ano} mes={mes} />

      {contaAberta && (
        <ContaModal auth={auth} onClose={() => setContaAberta(false)} onLogout={sair}
          onAtualizarPerfil={(patch) => setAuth(a => ({ ...a, ...patch }))}
          idioma={idioma} onTrocarIdioma={trocarIdioma} />
      )}

      {gerenciarFilhos && (
        <FilhosModal
          filhos={filhos}
          onClose={() => setGerenciarFilhos(false)}
          onMudou={carregarFilhos}
          setErro={setErro}
        />
      )}
    </div>
  );
}

// Card de boas-vindas para conta nova (sem filhos e sem aulas)
function BoasVindas({ onAdicionarFilhos, onDispensar }) {
  return (
    <div className="boasvindas-card">
      <button className="boasvindas-fechar" onClick={onDispensar} aria-label={window.t('bv.dispensar')}>✕</button>
      <h3 className="boasvindas-titulo">🌞 {window.t('bv.titulo')}</h3>
      <p className="boasvindas-sub">{window.t('bv.sub')}</p>
      <ol className="boasvindas-passos">
        <li>
          <span className="boasvindas-emoji">🧒</span>
          <div className="boasvindas-texto">
            <strong>{window.t('bv.passo1T')}</strong>
            <small>{window.t('bv.passo1S')}</small>
          </div>
          <button className="btn-pequeno" onClick={onAdicionarFilhos}>{window.t('bv.adicionar')}</button>
        </li>
        <li>
          <span className="boasvindas-emoji">📅</span>
          <div className="boasvindas-texto">
            <strong>{window.t('bv.passo2T')}</strong>
            <small>{window.t('bv.passo2S')}</small>
          </div>
        </li>
        <li>
          <span className="boasvindas-emoji">📸</span>
          <div className="boasvindas-texto">
            <strong>{window.t('bv.passo3T')}</strong>
            <small>{window.t('bv.passo3S')}</small>
          </div>
        </li>
      </ol>
    </div>
  );
}

function Calendario({ anotacoes, meta, nomeMes, onAbrirDia, mostrarFds, onToggleFds }) {
  // Só dias úteis (seg–sex): fim de semana fica fora do grid
  const diaSemana = (dia) => (meta.firstDay + dia - 1) % 7; // 0=Dom … 6=Sáb
  const ehUtil = (dia) => diaSemana(dia) >= 1 && diaSemana(dia) <= 5;
  const ehFds = (dia) => !ehUtil(dia);
  // Com "mostrar fins de semana" marcado, o grid volta a 7 colunas (Dom–Sáb)
  const incluir = (dia) => mostrarFds || ehUtil(dia);
  const colunas = mostrarFds ? 7 : 5;
  const colDe = (dia) => mostrarFds ? diaSemana(dia) : diaSemana(dia) - 1;
  const cells = [];
  let primeiro = 1;
  while (primeiro <= meta.daysInMonth && !incluir(primeiro)) primeiro++;
  const padInicio = primeiro <= meta.daysInMonth ? colDe(primeiro) : 0;
  for (let i = 0; i < padInicio; i++) cells.push(<div key={`pre-${i}`} className="cal-cell-big outside"></div>);
  for (let dia = 1; dia <= meta.daysInMonth; dia++) {
    if (!incluir(dia)) continue;
    const dados = anotacoes[dia];
    const tem = !!dados;
    const emoji = tem ? (dados.emoji || EMOJIS_DIA[(dia - 1) % EMOJIS_DIA.length]) : null;
    const nFotos = tem ? (dados.fotos || []).length : 0;
    const concluida = tem && dados.concluida;
    const cls = !tem ? 'vazio' : (concluida ? 'preenchido concluida' : 'preenchido planejado');
    cells.push(
      <button key={dia} className={`cal-cell-big ${cls} ${ehFds(dia) ? 'fds-dia' : ''}`} onClick={() => onAbrirDia(dia)}>
        <div className="cal-dia-num">{dia}</div>
        {tem ? (
          <>
            <div className="cal-emoji">{emoji}</div>
            <div className="cal-assunto" title={dados.assunto}>{dados.assunto}</div>
            {concluida && <div className="cal-feito" aria-label={window.t('cal.concluida')}>✓</div>}
            {nFotos > 0 && <div className="cal-foto-tag"><NavIcone nome="memorias" /></div>}
          </>
        ) : (<div className="cal-plus">+</div>)}
      </button>
    );
  }
  const remainder = (colunas - (cells.length % colunas)) % colunas;
  for (let i = 0; i < remainder; i++) cells.push(<div key={`post-${i}`} className="cal-cell-big outside"></div>);
  const dows = window.SoluI18n ? window.SoluI18n.diasSemana(mostrarFds) : (mostrarFds ? DOW : DOW.slice(1, 6));
  return (
    <div className="calendario-wrap">
      <div className="calendario-instrucao">
        {window.t('cal.instrucao')}
      </div>
      <div className={`cal-grid-big ${mostrarFds ? 'com-fds' : ''}`}>
        {dows.map((d, i) => <div key={i} className="cal-dow-big">{d}</div>)}
        {cells}
      </div>
      <label className="fds-toggle">
        <input
          type="checkbox"
          checked={mostrarFds}
          onChange={(e) => onToggleFds(e.target.checked)}
        />
        <span>{window.t('cal.mostrarFds')}</span>
      </label>
    </div>
  );
}

window.App = App;
