/* ============================================================
   EDUCARE — Mock data (pacientes de exemplo)
   ============================================================ */
(function () {
  /* ------------------------------------------------------------
     Data/hora — SEMPRE no fuso local (America/Sao_Paulo do aparelho).
     Comparar por ano/mês/dia locais, nunca por timestamp UTC (senão o
     "dia" vira às 21h). Fonte única de "hoje" para todo o app.
     ------------------------------------------------------------ */
  const pad2 = (n) => String(n).padStart(2, "0");
  const isoLocal = (dt) => `${dt.getFullYear()}-${pad2(dt.getMonth() + 1)}-${pad2(dt.getDate())}`;
  const todayLocal = () => { const n = new Date(); return new Date(n.getFullYear(), n.getMonth(), n.getDate()); };
  const todayISO = () => isoLocal(new Date());
  const isoToLocalDate = (s) => { if (!s) return null; const [y, m, d] = String(s).split("-").map(Number); return new Date(y, (m || 1) - 1, d || 1); };
  Object.assign(window, { isoLocal, todayLocal, todayISO, isoToLocalDate });

  const PATIENTS = [];

  const STATUS = {
    ativo:     { label: "Ativo",      cls: "ok" },
    avaliacao: { label: "Avaliação",  cls: "warn" },
    pausado:   { label: "Pausado",    cls: "muted" },
  };

  // Histórico de atendimentos (timeline) por paciente — exemplo p/ Helena
  const SESSIONS = {};

  // Avaliações aplicadas — exemplo p/ Helena
  const ASSESSMENTS = {};

  // Planejamentos — exemplo p/ Helena
  const PLANS = {};

  const GRADES = ["Educação Infantil", "1º ano", "2º ano", "3º ano", "4º ano", "5º ano", "6º ano", "7º ano", "8º ano", "9º ano", "Ensino Médio"];

  // Eventos da agenda — Junho 2026 (hoje = 4)
  // type: agendado | confirmado | compromisso | bloqueado | aniversario | feriado
  const EVENTS = [];

  // Atendimentos registrados (log global) — derivado dos pacientes
  const ATEND = [];

  const ST_LABEL = {
    agendado: "Agendado", confirmado: "Confirmado", compromisso: "Compromisso",
    bloqueado: "Bloqueado", aniversario: "Aniversário", feriado: "Feriado",
    realizado: "Sessão realizada", falta: "Falta",
  };

  // Instrumentos de avaliação (grid de cards)
  const INSTRUMENTS = [
    { name: "Anamnese Adulto", icon: "folderHeart", uses: 0, fullpage: true, test: "anamnese",
      desc: "Histórico completo de desenvolvimento, saúde, escola e família — a base que alimenta as demais avaliações e o planejamento clínico." },
    { name: "Anamnese Infantil", icon: "folderHeart", uses: 0, fullpage: true, test: "anamnese-infantil", badge: "Novo",
      desc: "Entrevista com os pais ou responsáveis — histórico de desenvolvimento, saúde, rotina, escolarização e dinâmica familiar da criança." },
    { name: "Teste de Hayling Infantil", icon: "clipboardList", uses: 0, fullpage: true, test: "hayling",
      desc: "Avaliação de iniciação e inibição verbal (funções executivas): completar frases na Parte A e inibir a resposta automática na Parte B." },
    { name: "Teste ABC", icon: "clipboardList", uses: 0, fullpage: true, test: "abc",
      desc: "Maturidade para a aprendizagem da leitura e escrita (Lourenço Filho): 8 subtestes que somam o Nível de Maturidade (0–24)." },
    { name: "Tarefas de Fluência Verbal (TFV)", icon: "clipboardList", uses: 0, fullpage: true, test: "tfv",
      desc: "Linguagem e funções executivas: fluência verbal livre, fonêmica (P) e semântica (roupas), registradas em blocos de 30 segundos." },
    { name: "ASRS-18 — Rastreio de TDAH (adultos)", icon: "clipboardList", uses: 0, fullpage: true, test: "asrs",
      desc: "Autoteste de TDAH em adultos (escala da OMS): 18 questões em Parte A (rastreio) e Parte B (apoio). Instrumento de rastreio, não diagnóstico." },
    { name: "IAR — Repertório Básico para a Alfabetização", icon: "clipboardList", uses: 0, fullpage: true, test: "iar",
      desc: "Instrumento de Avaliação do Repertório Básico para a Alfabetização (Leite): registro das 13 áreas com o código de cores azul/verde/vermelho." },
    { name: "Seguir Instruções — Atenção e Memória de Trabalho (6 anos)", icon: "clipboardList", uses: 0, fullpage: true, test: "seguir", badge: "Novo",
      desc: "Atividade lúdica que avalia atenção, memória de trabalho, compreensão e a capacidade de seguir instruções de 2 e 3 etapas. Aplicada em papel." },
    { name: "DNOI — Denominação Oral de Imagens (4 a 12 anos)", icon: "chat", uses: 0, fullpage: true, test: "dnoi", badge: "Novo",
      desc: "Atividade que avalia a nomeação de objetos e imagens, vocabulário expressivo e acesso lexical. Aplicada em papel." },
    { name: "Fluência Leitora (1º ao 5º ano)", icon: "book", uses: 0, fullpage: true, test: "fluencia", badge: "Novo",
      desc: "Avalia a velocidade e precisão da leitura oral de palavras e texto, com classificação por palavras lidas por minuto." },
    { name: "GAN — Geração Aleatória de Números (6 a 12 anos)", icon: "activity", uses: 0, fullpage: true, test: "gan", badge: "Novo",
      desc: "Avalia funções executivas (memória de trabalho e inibição) por meio da evocação aleatória de números ao ritmo de estímulos sonoros." },
  ];

  // O que cada plano permite vive no banco (billing_plans) e chega à
  // tela por window.EducareBilling. Não existe mais cota mensal aqui:
  // avaliação, anamnese, planejamento e atendimento são ilimitados em
  // todos os planos, e o que separa os planos é paciente ativo.

  // Biblioteca de recursos
  const RESOURCES = [
    { title: "Caderno de São João", sub: "10 a 12 anos", cat: "Datas comemorativas", pro: true, c1: "#F2801F", c2: "#FBBF24" },
    { title: "Caderno de São João", sub: "7 a 9 anos", cat: "Datas comemorativas", pro: true, c1: "#EF4444", c2: "#FB923C" },
    { title: "Caderno de São João", sub: "4 a 6 anos", cat: "Datas comemorativas", pro: true, c1: "#F2801F", c2: "#FCD34D" },
    { title: "Consciência Fonológica", sub: "Atividades imprimíveis", cat: "Alfabetização", pro: true, c1: "#0F766E", c2: "#14B8A6" },
    { title: "Jogos de Numeração", sub: "Números de 1 a 20", cat: "Matemática", pro: false, c1: "#2563EB", c2: "#38BDF8" },
    { title: "Cartões de Emoções", sub: "Reconhecimento socioemocional", cat: "Socioemocional", pro: false, c1: "#DB2777", c2: "#FB7185" },
    { title: "Kit Funções Executivas", sub: "Atenção e organização", cat: "TDAH", pro: true, c1: "#7C3AED", c2: "#A78BFA" },
    { title: "Histórias Sociais", sub: "Rotinas e interação", cat: "TEA", pro: true, c1: "#0891B2", c2: "#22D3EE" },
    { title: "Modelo de Relatório", sub: "Para pais e escola", cat: "Geral", pro: false, c1: "#475569", c2: "#94A3B8" },
  ];

  // Modelo de Relatório resource closes the RESOURCES array above.

  // Financeiro — recebimentos por sessão
  const PAYMENTS = [];
  const FIN_SUMMARY = { recebido: 0, pendente: 0, mes: 0, sessoes: 0 };

  // Despesas (saídas) da empresa
  const DESPESA_CATS = ["Aluguel", "Materiais", "Marketing", "Software", "Impostos", "Equipe", "Transporte", "Outros"];
  const DESPESAS = [];
  // Histórico mensal (entradas x saídas) — últimos 6 meses
  const FIN_MONTHLY = [];

  // WhatsApp — automações + histórico
  const WA_AUTOMATIONS = [
    { id: "confirm", title: "Confirmação de sessão", desc: "Enviada 24h antes da sessão pedindo confirmação do responsável.", on: true, when: "24h antes", icon: "calendar" },
    { id: "lembrete", title: "Lembrete no dia", desc: "Lembrete amigável na manhã do dia da sessão.", on: true, when: "Manhã do dia", icon: "bell" },
    { id: "retorno", title: "Reagendamento de faltas", desc: "Mensagem para remarcar quando o paciente falta.", on: false, when: "Após falta", icon: "arrowRight" },
    { id: "aniversario", title: "Mensagem de aniversário", desc: "Parabéns automático no aniversário da criança.", on: false, when: "No aniversário", icon: "heart" },
    { id: "pagamento", title: "Lembrete de pagamento", desc: "Aviso de mensalidade/sessão pendente.", on: false, when: "Vencimento", icon: "wallet" },
  ];
  const WA_LOG = [];

  // Modo Clínica — profissionais da conta
  const PROFESSIONALS = [];
  // paciente → profissional responsável
  const PROF_OF = {};

  /* ------------------------------------------------------------
     Perfil profissional do usuário — usado no cabeçalho/rodapé dos
     documentos (relatórios, sínteses, PDFs). Editável em Configurações.
     Mescla o usuário autenticado (window.__USER) com os campos salvos.
     ------------------------------------------------------------ */
  const PROFILE_KEY = "educare-perfil-prof";
  function loadProfile() { try { return JSON.parse(localStorage.getItem(PROFILE_KEY) || "null") || {}; } catch (e) { return {}; } }
  function saveProfile(patch) {
    const cur = loadProfile();
    const next = Object.assign({}, cur, patch || {});
    try { localStorage.setItem(PROFILE_KEY, JSON.stringify(next)); } catch (e) { /* */ }
    return next;
  }
  function getProfile() {
    const s = loadProfile();
    const u = window.__USER || {};
    const owner = PROFESSIONALS.find((x) => x.owner) || {};
    const name = (s.name || u.name || "").trim();
    const profession = (s.profession || u.role || "").trim();
    const council = (s.council || "").trim();
    const reg = (s.reg || "").trim();
    // Linha de registro: só existe se o profissional preencheu o número (nunca placeholder).
    const regLine = reg ? (council ? `${council} ${reg}` : reg) : "";
    return { name, profession, council, reg, regLine, phone: (s.phone || "").trim(), email: (s.email || u.email || "").trim() };
  }
  Object.assign(window, { getProfile, saveProfile, loadProfile });

  /* ------------------------------------------------------------
     Persistência de pacientes cadastrados pelo usuário
     (fonte de verdade: Supabase via app/db.jsx)
     ------------------------------------------------------------ */
  const AVATAR_COLORS = ["a", "b", "c", "d", "e"];

  function ageFromBirth(iso) {
    if (!iso) return "—";
    const b = new Date(iso + "T00:00:00");
    if (isNaN(b.getTime())) return "—";
    const now = new Date();
    let a = now.getFullYear() - b.getFullYear();
    const mm = now.getMonth() - b.getMonth();
    if (mm < 0 || (mm === 0 && now.getDate() < b.getDate())) a--;
    return a >= 0 ? a : "—";
  }

  function initialsFrom(name) {
    const parts = (name || "").trim().split(/\s+/).filter(Boolean);
    if (!parts.length) return "?";
    const first = parts[0][0] || "";
    const last = parts.length > 1 ? parts[parts.length - 1][0] : "";
    return (first + last).toUpperCase();
  }

  function erroDeGravacao(e) {
    console.error("[Educare] gravacao falhou:", e);
    window.dispatchEvent(new CustomEvent("educare-db-erro", { detail: (e && e.message) || "Nao foi possivel salvar no servidor." }));
  }
  const gravar = (p) => { p.catch(erroDeGravacao); };

  function addPatient(data, prof) {
    const name = (data.name || "").trim();
    let guardian = (data.guardian || "").trim();
    let guardianRel = "Responsável";
    const m = guardian.match(/^(.*?)\s*\(([^)]+)\)\s*$/);
    if (m) { guardian = m[1].trim(); guardianRel = m[2].trim(); }
    const diagnoses = (data.diagnoses || "").trim();
    /* Sem profissional escolhido, vincula ao titular da conta. O id fixo
       "ademir" que existia aqui vinha dos dados de demonstração e não é
       UUID — o banco rejeitava o insert inteiro. */
    const profId = prof && prof !== "all" ? prof : ((PROFESSIONALS[0] || {}).id || "");

    const p = {
      id: window.EducareDB.novoId(),
      name: name || "Paciente sem nome",
      initials: initialsFrom(name),
      color: AVATAR_COLORS[PATIENTS.length % AVATAR_COLORS.length],
      birth: data.birth || "",
      age: ageFromBirth(data.birth),
      grade: data.grade || "—",
      school: (data.school || "").trim() || "—",
      escola: data.escolaTipo || "",
      guardian: guardian || "—",
      guardianRel,
      phone: (data.phone || "").trim() || "—",
      /* Vinha fixo em "avaliacao", ignorando o formulário — nenhum
         paciente jamais ficava "ativo" e o indicador "Pacientes ativos"
         do painel travava em zero para todo mundo. */
      status: STATUS[data.status] ? data.status : "avaliacao",
      tags: diagnoses ? diagnoses.split(/[,;]+/).map((t) => t.trim()).filter(Boolean) : [],
      complaint: (data.complaint || "").trim() || "—",
      referredBy: (data.referredBy || "").trim() || "—",
      meds: (data.meds || "").trim() || "—",
      sessions: 0, lastSession: "—", nextSession: "—",
      anamnese: false,
      moodSeries: [], goals: [], attendance: 0,
      fee: Math.max(0, Math.round(Number(data.fee)) || 0),
      billMode: data.billMode === "sessao" ? "sessao" : "mensal",
      /* Nasce ativo, ocupando vaga. Arquivar é sempre decisão da
         profissional, nunca do sistema. */
      arquivado: false,
      _user: true, _prof: profId,
    };

    PATIENTS.push(p);
    PROF_OF[p.id] = profId;
    gravar(window.EducareDB.salvarPaciente(p));
    return p;
  }

  /* ------------------------------------------------------------
     Persistência de agendamentos criados pelo usuário
     ------------------------------------------------------------ */

  function newEventId() { return window.EducareDB.novoId(); }

  function addEvent(data) {
    const ev = {
      id: newEventId(),
      date: data.date,
      start: data.start || "",
      end: data.end || "",
      title: (data.title || "").trim() || "Compromisso",
      type: data.type || "agendado",
      _user: true,
    };
    if (data.pid) ev.pid = data.pid;
    if (data.allDay) ev.allDay = true;
    EVENTS.push(ev);
    gravar(window.EducareDB.salvarEvento(ev));
    return ev;
  }

  function updateEvent(id, patch) {
    const e = EVENTS.find((x) => x.id === id); if (!e) return null;
    Object.assign(e, patch);
    if (patch && Object.prototype.hasOwnProperty.call(patch, "pid") && !patch.pid) delete e.pid;
    gravar(window.EducareDB.atualizarEvento(e));
    return e;
  }
  function removeEvent(id) {
    const i = EVENTS.findIndex((x) => x.id === id); if (i < 0) return;
    EVENTS.splice(i, 1);
    gravar(window.EducareDB.apagarEvento(id));
  }



  /* ------------------------------------------------------------
     Persistência de atendimentos registrados pelo usuário
     (alimenta a lista global ATEND e o histórico SESSIONS[pid])
     ------------------------------------------------------------ */
  const MON_SHORT_AT = ["jan", "fev", "mar", "abr", "mai", "jun", "jul", "ago", "set", "out", "nov", "dez"];
  const USER_ATEND = [];

  function fmtLongDate(iso) {
    if (!iso) return "—";
    const [y, m, d] = iso.split("-");
    if (!y || !m || !d) return iso;
    return `${d} ${MON_SHORT_AT[parseInt(m, 10) - 1]} ${y}`;
  }

  function applyAtend(rec) {
    const dateLong = fmtLongDate(rec.dateIso);
    if (!rec.id) rec.id = "at-" + Math.random().toString(36).slice(2, 9);
    ATEND.unshift({ id: rec.id, pid: rec.pid, date: dateLong, dur: rec.dur, mood: rec.mood, focus: rec.focus, status: rec.status || "realizado", _user: true });
    if (!SESSIONS[rec.pid]) SESSIONS[rec.pid] = [];
    SESSIONS[rec.pid].unshift({
      date: dateLong, dur: rec.dur, mood: rec.mood, focus: rec.focus,
      activities: rec.activities || "—", notes: rec.notes || "—", next: rec.next || "—",
    });
  }

  function addAtendimento(data) {
    const rec = {
      id: window.EducareDB.novoId(),
      status: "realizado",
      pid: data.pid,
      dateIso: data.date || todayISO(),
      dur: parseInt(data.dur, 10) || 50,
      mood: data.mood || 3,
      focus: (data.focus || "").trim() || "Atendimento",
      activities: (data.activities || "").trim(),
      notes: (data.notes || "").trim(),
      next: (data.next || "").trim(),
    };
    USER_ATEND.unshift(rec);
    applyAtend(rec);
    gravar(window.EducareDB.salvarAtendimento(rec));
    return rec;
  }

  function setAtendStatus(id, status) {
    const a = ATEND.find((x) => x.id === id); if (a) a.status = status;
    const r = USER_ATEND.find((x) => x.id === id); if (r) r.status = status;
    gravar(window.EducareDB.atualizarStatusAtendimento(id, status));
  }

  /* ------------------------------------------------------------
     Persistência de planejamentos vinculados ao prontuário
     ------------------------------------------------------------ */
  const USER_PLANS = [];

  function applyPlan(pid, entry) {
    if (!PLANS[pid]) PLANS[pid] = [];
    PLANS[pid].unshift(entry);
  }

  function addPlan(plan, cfg) {
    const entry = {
      id: window.EducareDB.novoId(),
      title: plan.title,
      level: cfg.nivel,
      sessions: cfg.sessions,
      freq: cfg.freq,
      created: fmtLongDate(todayISO()),
      status: "Em andamento",
      progress: 0,
      goal: plan.goal,
    };
    USER_PLANS.unshift({ pid: cfg.pid, entry });
    applyPlan(cfg.pid, entry);
    gravar(window.EducareDB.salvarPlano(cfg.pid, entry));
    return entry;
  }

  /* ------------------------------------------------------------
     Persistência de avaliações aplicadas pelo usuário
     ------------------------------------------------------------ */
  const USER_AVAL = [];

  function applyAval(rec) {
    if (!ASSESSMENTS[rec.pid]) ASSESSMENTS[rec.pid] = [];
    ASSESSMENTS[rec.pid].unshift({
      id: rec.id, name: rec.name, date: fmtLongDate(rec.dateIso),
      result: rec.result || "Concluída", status: "done", note: rec.note || "",
      sheet: rec.sheet || null,
    });
    const ins = INSTRUMENTS.find((x) => x.name === rec.name);
    if (ins) ins.uses = (ins.uses || 0) + 1;
  }

  function addAvaliacao(data) {
    const rec = {
      id: window.EducareDB.novoId(),
      pid: data.pid, name: data.name,
      dateIso: data.date || todayISO(),
      result: (data.result || "").trim() || "Concluída",
      note: (data.note || "").trim(),
      sheet: data.sheet || null,
    };
    USER_AVAL.unshift(rec);
    applyAval(rec);
    gravar(window.EducareDB.salvarAvaliacao(rec));
    return rec;
  }

  /* Reescreve uma avaliação já salva — usado quando o laudo da IA fica pronto
     depois que o teste foi gravado no prontuário. Mantém o mesmo registro (id),
     em memória e no banco, para a ficha refletir o resultado final. */
  function atualizarAvaliacao(id, data) {
    const rec = USER_AVAL.find((r) => r.id === id);
    if (!rec) return null;
    if (data.result != null) rec.result = (data.result || "").trim() || "Concluída";
    if (data.note != null) rec.note = (data.note || "").trim();
    if (data.sheet !== undefined) rec.sheet = data.sheet || null;

    const lista = ASSESSMENTS[rec.pid] || [];
    const item = lista.find((x) => x.id === id);
    if (item) { item.result = rec.result; item.note = rec.note; item.sheet = rec.sheet; }

    gravar(window.EducareDB.atualizarAvaliacao(rec));
    return rec;
  }

  /* Muda a situação de acompanhamento do paciente (ativo, em avaliação,
     pausado). Sem isto o status escolhido no cadastro ficava congelado
     para sempre. */
  function definirStatusPaciente(id, status) {
    if (!STATUS[status]) return null;
    const p = PATIENTS.find((x) => x.id === id);
    if (!p) return null;
    p.status = status;
    gravar(window.EducareDB.atualizarPaciente(p));
    return p;
  }

  /* Arquiva ou reativa. Arquivar libera a vaga do plano e NÃO apaga
     nada: o prontuário continua inteiro, legível e exportável.

     A mudança em memória é otimista, como no resto do app, mas aqui ela
     é DESFEITA se o banco recusar. Desarquivar acima do teto é barrado
     pelo gatilho, e deixar o paciente aparecendo como ativo na tela
     depois de o banco ter dito não faria a profissional achar que tem
     uma vaga que não tem.

     Depois de gravar, o estado da assinatura é relido: quem muda é a
     contagem de pacientes ativos, e ela alimenta o "N de N" da tela. */
  function definirArquivamentoPaciente(id, arquivado) {
    const p = PATIENTS.find((x) => x.id === id);
    if (!p) return null;

    const antes = !!p.arquivado;
    if (antes === !!arquivado) return p;
    p.arquivado = !!arquivado;

    window.EducareDB.arquivarPaciente(id, arquivado)
      .then(() => {
        if (window.EducareBilling) window.EducareBilling.carregar();
      })
      .catch((e) => {
        p.arquivado = antes;
        erroDeGravacao(e);
      });

    return p;
  }

  /* Edição do cadastro (aba Dados do prontuário). Atualiza SÓ os campos
     editáveis, deriva idade/iniciais e persiste pelo MESMO serviço do
     cadastro (EducareDB.atualizarPaciente) — sem tocar avaliações,
     anamnese, histórico ou sessões (que vivem em outras coleções). */
  const CAMPOS_EDITAVEIS = ["name", "birth", "grade", "school", "guardian", "guardianRel", "phone", "referredBy", "status", "complaint"];
  function updatePatient(id, patch) {
    const p = PATIENTS.find((x) => x.id === id);
    if (!p) return null;
    CAMPOS_EDITAVEIS.forEach((k) => { if (patch && Object.prototype.hasOwnProperty.call(patch, k)) p[k] = patch[k]; });
    if (!STATUS[p.status]) p.status = "avaliacao";
    p.age = ageFromBirth(p.birth);
    p.initials = initialsFrom(p.name);
    gravar(window.EducareDB.atualizarPaciente(p));
    return p;
  }

  /* ------------------------------------------------------------
     Histórico mensal do financeiro (entradas x saídas)

     FIN_MONTHLY era uma lista fixa dos dados de demonstração. Quando
     eles saíram, o array ficou vazio e nunca mais foi preenchido — o
     gráfico "Evolução da receita" do painel e o "Entradas × Saídas" do
     financeiro não desenhavam nada, por mais lançamentos que houvesse.
     Agora é derivado dos próprios lançamentos, a cada mudança.
     ------------------------------------------------------------ */
  const MESES_CURTOS = ["jan", "fev", "mar", "abr", "mai", "jun", "jul", "ago", "set", "out", "nov", "dez"];
  const MESES_NO_GRAFICO = 6;

  function recalcularHistoricoMensal() {
    const hoje = new Date();
    const baldes = {};
    const ordem = [];

    for (let i = MESES_NO_GRAFICO - 1; i >= 0; i--) {
      const d = new Date(hoje.getFullYear(), hoje.getMonth() - i, 1);
      const chave = `${d.getFullYear()}-${d.getMonth()}`;
      baldes[chave] = { m: MESES_CURTOS[d.getMonth()], receita: 0, despesa: 0 };
      ordem.push(chave);
    }

    const somar = (lista, campo) => {
      (lista || []).forEach((item) => {
        const iso = item.dateIso || item.date;
        if (typeof iso !== "string" || !/^\d{4}-\d{2}-\d{2}/.test(iso)) return;
        const [ano, mes] = iso.split("-");
        const chave = `${Number(ano)}-${Number(mes) - 1}`;
        if (baldes[chave]) baldes[chave][campo] += Number(item.value) || 0;
      });
    };

    /* Só entra o que foi efetivamente recebido — pendência não é receita. */
    somar(PAYMENTS.filter((p) => !p.status || p.status === "pago"), "receita");
    somar(DESPESAS, "despesa");

    /* Mantém a MESMA referência do array: o app inteiro já aponta para
       ela via window.FIN_MONTHLY. */
    FIN_MONTHLY.length = 0;
    ordem.forEach((chave) => FIN_MONTHLY.push(baldes[chave]));
  }

  /* ------------------------------------------------------------
     Persistência de lançamentos financeiros (recebimentos e despesas)
     ------------------------------------------------------------ */
  function addRecebimento(data) {
    const iso = data.date || todayISO();
    const item = {
      id: window.EducareDB.novoId(),
      pid: data.pid || "", dateIso: iso, date: fmtLongDate(iso),
      desc: (data.desc || "").trim() || "Recebimento",
      value: Math.round(Number(data.value)) || 0,
      status: data.status || "pago", method: data.method || "—", _user: true,
    };
    PAYMENTS.unshift(item);
    gravar(window.EducareDB.salvarPagamento({ ...item, date: iso }));
    recalcularHistoricoMensal();
    return item;
  }

  function addDespesa(data) {
    const iso = data.date || todayISO();
    const item = {
      id: window.EducareDB.novoId(),
      dateIso: iso, date: fmtLongDate(iso),
      desc: (data.desc || "").trim() || "Despesa",
      category: data.category || "Outros", cat: data.category || "Outros",
      value: Math.round(Number(data.value)) || 0,
      method: data.method || "—", _user: true,
    };
    DESPESAS.unshift(item);
    gravar(window.EducareDB.salvarDespesa({ ...item, date: iso }));
    recalcularHistoricoMensal();
    return item;
  }

  /* ------------------------------------------------------------
     Hidratação: chamada uma vez no boot com o que veio do banco.
     Repõe o conteúdo preservando as MESMAS referências de array e
     objeto — o app inteiro já aponta para elas via window.
     ------------------------------------------------------------ */
  function hidratar(dados) {
    [PATIENTS, EVENTS, ATEND, PAYMENTS, DESPESAS, PROFESSIONALS, USER_ATEND, USER_PLANS, USER_AVAL]
      .forEach((a) => { a.length = 0; });
    [SESSIONS, ASSESSMENTS, PLANS, PROF_OF].forEach((o) => Object.keys(o).forEach((k) => delete o[k]));
    INSTRUMENTS.forEach((i) => { i.uses = 0; });

    (dados.profissionais || []).forEach((p) => PROFESSIONALS.push(p));
    (dados.pacientes || []).forEach((p) => { PATIENTS.push(p); PROF_OF[p.id] = p._prof || ""; });
    (dados.eventos || []).forEach((e) => EVENTS.push(e));
    (dados.atendimentos || []).forEach((r) => { USER_ATEND.unshift(r); applyAtend(r); });
    (dados.avaliacoes || []).forEach((r) => { USER_AVAL.unshift(r); applyAval(r); });
    (dados.planos || []).forEach((r) => { USER_PLANS.unshift(r); applyPlan(r.pid, r.entry); });
    (dados.pagamentos || []).forEach((p) => PAYMENTS.push({ ...p, dateIso: p.date, date: fmtLongDate(p.date), _user: true }));
    (dados.despesas || []).forEach((d) => DESPESAS.push({ ...d, dateIso: d.date, date: fmtLongDate(d.date), category: d.cat, _user: true }));

    /* Derivados: o banco guarda as sessões; humor e frequência saem delas. */
    PATIENTS.forEach((p) => {
      const s = SESSIONS[p.id] || [];
      p.sessions = s.length;
      p.moodSeries = s.slice().reverse().map((x) => x.mood).filter((m) => m != null);
      p.lastSession = s.length ? s[0].date : "—";
    });

    /* Resumo financeiro a partir dos lançamentos reais. */
    const pago = PAYMENTS.filter((p) => p.status === "pago").reduce((t, p) => t + (Number(p.value) || 0), 0);
    const pend = PAYMENTS.filter((p) => p.status !== "pago").reduce((t, p) => t + (Number(p.value) || 0), 0);
    FIN_SUMMARY.recebido = pago; FIN_SUMMARY.pendente = pend;
    FIN_SUMMARY.mes = pago + pend; FIN_SUMMARY.sessoes = ATEND.length;

    recalcularHistoricoMensal();

    /* As cobranças pertencem ao billing.jsx, que as consome deste ponto. */
    window.__COBRANCAS_DO_BANCO = dados.cobrancas || [];
  }

  Object.assign(window, { PATIENTS, STATUS, SESSIONS, ASSESSMENTS, PLANS, GRADES, EVENTS, ATEND, ST_LABEL, INSTRUMENTS, RESOURCES, PAYMENTS, FIN_SUMMARY, DESPESAS, DESPESA_CATS, FIN_MONTHLY, WA_AUTOMATIONS, WA_LOG, PROFESSIONALS, PROF_OF, hidratarDados: hidratar, ageFromBirth, fmtLongDate, addPatient, definirStatusPaciente, definirArquivamentoPaciente, updatePatient, addEvent, updateEvent, removeEvent, addAtendimento, addPlan, addAvaliacao, atualizarAvaliacao, addRecebimento, addDespesa, setAtendStatus });
})();
