// Data layer — localStorage persistence + sample seed data
const STORAGE_KEY = "agenda.v1";

const uid = () => Math.random().toString(36).slice(2, 10);

// --- Seed data ---------------------------------------------------------------
const today = new Date();
const fmtISO = (d) => d.toISOString().slice(0, 10);
const dayOffset = (n) => {
  const d = new Date(today);
  d.setDate(d.getDate() + n);
  return fmtISO(d);
};

const seedData = () => {
  const p1 = uid(), p2 = uid(), p3 = uid(), p4 = uid(), p5 = uid(), p6 = uid();
  return {
    people: [
      { id: p1, name: "Marina Tavares", phone: "11984521029", email: "marina.t@email.com", tag: "Lead quente", source: "Instagram", note: "Procura mentoria de carreira; trabalha com design." },
      { id: p2, name: "Eduardo Lins", phone: "21998123344", email: "eduardo.lins@email.com", tag: "Cliente", source: "Indicação", note: "Sessão #4. Já fechou pacote trimestral." },
      { id: p3, name: "Júlia Pacheco", phone: "47991027711", email: "ju.pacheco@email.com", tag: "Avaliando", source: "YouTube", note: "Pediu para reagendar uma vez." },
      { id: p4, name: "Rafael Coutinho", phone: "31982210093", email: "rafa.cout@email.com", tag: "Cliente", source: "WhatsApp", note: "Acompanhamento mensal." },
      { id: p5, name: "Tatiana Brum", phone: "51987774421", email: "tati.brum@email.com", tag: "Lead frio", source: "LinkedIn", note: "Demonstrou interesse, ainda não respondeu follow-up." },
      { id: p6, name: "Hélio Andrade", phone: "85991105678", email: "helio@email.com", tag: "Lead quente", source: "Indicação", note: "Quer começar em janeiro." },
    ],
    appointments: [
      { id: uid(), title: "Sessão de avaliação", personId: p1, date: dayOffset(0), time: "09:30", duration: 60, type: "Entrevista", status: "confirmado", location: "https://meet.google.com/abc-defg-hij", notes: "Primeira conversa, levantar dores principais.", reminder: 30, price: 0, audit: [] },
      { id: uid(), title: "Mentoria recorrente", personId: p2, date: dayOffset(0), time: "11:00", duration: 50, type: "Mentoria", status: "confirmado", location: "https://meet.google.com/xyz-abcd-efg", notes: "Revisar metas do trimestre.", reminder: 15, price: 350, audit: [] },
      { id: uid(), title: "Follow-up comercial", personId: p5, date: dayOffset(0), time: "14:30", duration: 30, type: "Follow-up", status: "agendado", location: "Ligação WhatsApp", notes: "Apresentar pacote trimestral.", reminder: 10, price: 0, audit: [] },
      { id: uid(), title: "Sessão profunda", personId: p4, date: dayOffset(0), time: "16:00", duration: 90, type: "Consulta", status: "agendado", location: "https://meet.google.com/qrs-tuvw-xyz", notes: "Bloco extenso, separar caderno.", reminder: 30, price: 480, audit: [] },
      { id: uid(), title: "Entrevista gratuita", personId: p3, date: dayOffset(1), time: "10:00", duration: 45, type: "Entrevista", status: "agendado", location: "https://meet.google.com/aaa-bbb-ccc", notes: "", reminder: 30, price: 0, audit: [] },
      { id: uid(), title: "Mentoria", personId: p2, date: dayOffset(1), time: "15:00", duration: 50, type: "Mentoria", status: "agendado", location: "https://meet.google.com/ddd-eee-fff", notes: "", reminder: 15, price: 350, audit: [] },
      { id: uid(), title: "Entrevista gratuita", personId: p6, date: dayOffset(2), time: "09:00", duration: 45, type: "Entrevista", status: "confirmado", location: "https://meet.google.com/ggg-hhh-iii", notes: "Lead quente. Vir preparado.", reminder: 60, price: 0, audit: [] },
      { id: uid(), title: "Reunião interna", personId: null, date: dayOffset(2), time: "14:00", duration: 60, type: "Reunião", status: "agendado", location: "Sala — Estúdio", notes: "Planejamento de conteúdo do mês.", reminder: 15, price: 0, audit: [] },
      { id: uid(), title: "Sessão", personId: p4, date: dayOffset(3), time: "10:30", duration: 60, type: "Consulta", status: "agendado", location: "https://meet.google.com/jjj-kkk-lll", notes: "", reminder: 30, price: 480, audit: [] },
      { id: uid(), title: "Mentoria", personId: p1, date: dayOffset(4), time: "11:00", duration: 50, type: "Mentoria", status: "agendado", location: "https://meet.google.com/mmm-nnn-ooo", notes: "", reminder: 15, price: 350, audit: [] },
      { id: uid(), title: "Bloco de gravação", personId: null, date: dayOffset(5), time: "09:00", duration: 180, type: "Reunião", status: "agendado", location: "Estúdio", notes: "Roteiros prontos no Notion.", reminder: 60, price: 0, audit: [] },
      { id: uid(), title: "Sessão anterior", personId: p2, date: dayOffset(-2), time: "11:00", duration: 50, type: "Mentoria", status: "realizado", location: "Meet", notes: "Sessão concluída. Próximos passos definidos.", reminder: 0, price: 350, audit: [] },
      { id: uid(), title: "Entrevista anterior", personId: p5, date: dayOffset(-5), time: "16:00", duration: 45, type: "Entrevista", status: "realizado", location: "Meet", notes: "Não converteu — manter no funil.", reminder: 0, price: 0, audit: [] },
      { id: uid(), title: "Mentoria do mês passado", personId: p2, date: dayOffset(-12), time: "11:00", duration: 50, type: "Mentoria", status: "realizado", location: "Meet", notes: "", reminder: 0, price: 350, audit: [] },
      { id: uid(), title: "Consulta arquivada", personId: p4, date: dayOffset(-18), time: "10:30", duration: 60, type: "Consulta", status: "realizado", location: "Meet", notes: "", reminder: 0, price: 480, audit: [] },
    ],
    tasks: [
      { id: uid(), title: "Enviar contrato para Eduardo", priority: "alta", category: "Comercial", note: "Pacote trimestral, condições já alinhadas." },
      { id: uid(), title: "Confirmar entrevistas de amanhã", priority: "alta", category: "Atendimento", note: "Mandar lembrete WhatsApp 1h antes." },
      { id: uid(), title: "Editar carrossel sobre rotina", priority: "andamento", category: "Conteúdo", note: "Pendente revisão de copy do slide 4." },
      { id: uid(), title: "Reorganizar planilha de leads", priority: "andamento", category: "Administrativo", note: "" },
      { id: uid(), title: "Estudar nova metodologia", priority: "depois", category: "Pessoal", note: "Reservar 2h na sexta." },
      { id: uid(), title: "Atualizar bio do site", priority: "depois", category: "Administrativo", note: "" },
    ],
    alerts: [
      { id: uid(), title: "Marina Tavares não confirmou sessão", level: "alta", when: dayOffset(0) + " 09:30", note: "Sem resposta no WhatsApp desde ontem." },
      { id: uid(), title: "Júlia Pacheco — reagendamento pendente", level: "média", when: dayOffset(1) + " 10:00", note: "Ela pediu para mover; ainda sem novo horário." },
      { id: uid(), title: "Backup de dados há mais de 7 dias", level: "baixa", when: dayOffset(-7), note: "Exportar JSON pelo menu." },
    ],
    notes: [
      { id: uid(), title: "Roteiro: o erro mais comum em começo de carreira", tags: ["carrossel", "atrair"], body: "Gancho: 'A maioria foca em ferramenta. Quase ninguém pensa em sistema.' Desenvolvimento em 6 slides." },
      { id: uid(), title: "Frase forte para abrir live", tags: ["live", "engajar"], body: "'A pergunta não é se você é capaz. É se você está disposto a passar pelo desconforto.'" },
      { id: uid(), title: "Estrutura de stories diários", tags: ["story", "nutrir"], body: "1. Bastidor da rotina — 2. Mini-aula — 3. Pergunta para o público — 4. CTA leve." },
      { id: uid(), title: "Ideias para próximo bloco de conteúdo", tags: ["editorial"], body: "Bloco 'Rotina real': hábitos, sono, foco, calendário, energia mental, vida social, descanso ativo." },
    ],
    settings: {
      theme: "dark",
      accent: "indigo",
      prices: { Entrevista: 0, Mentoria: 350, Consulta: 480, Reunião: 0, "Follow-up": 0 },
      lastBackup: null,
      notifiedReminders: [],
      seeded: true,
    },
  };
};

// --- Migration: ensure all fields exist on loaded data -----------------------
function migrate(data) {
  data.settings = data.settings || {};
  if (!data.settings.accent) data.settings.accent = "indigo";
  if (!data.settings.prices) data.settings.prices = { Entrevista: 0, Mentoria: 350, Consulta: 480, Reunião: 0, "Follow-up": 0 };
  if (data.settings.lastBackup === undefined) data.settings.lastBackup = null;
  if (!Array.isArray(data.settings.notifiedReminders)) data.settings.notifiedReminders = [];
  data.appointments = (data.appointments || []).map(a => ({
    ...a,
    price: typeof a.price === "number" ? a.price : (data.settings.prices[a.type] || 0),
    audit: Array.isArray(a.audit) ? a.audit : [],
  }));
  return data;
}

// --- Persistence -------------------------------------------------------------
function load() {
  try {
    const raw = localStorage.getItem(STORAGE_KEY);
    if (!raw) {
      const data = seedData();
      localStorage.setItem(STORAGE_KEY, JSON.stringify(data));
      return data;
    }
    return migrate(JSON.parse(raw));
  } catch (e) {
    console.warn("Falha ao carregar storage, reiniciando.", e);
    const data = seedData();
    localStorage.setItem(STORAGE_KEY, JSON.stringify(data));
    return data;
  }
}

function save(data) {
  localStorage.setItem(STORAGE_KEY, JSON.stringify(data));
}

function auditEntry(action, detail) {
  return { at: new Date().toISOString(), action, detail: detail || "" };
}

// --- API client --------------------------------------------------------------
async function api(method, path, body) {
  try {
    const r = await fetch("/api/" + path, {
      method,
      headers: body ? { "content-type": "application/json" } : undefined,
      body: body ? JSON.stringify(body) : undefined,
    });
    if (!r.ok && r.status !== 404) {
      // try to read error message
      try { const j = await r.json(); return j; } catch { return { ok: false, error: `HTTP ${r.status}` }; }
    }
    return await r.json();
  } catch (e) {
    return { ok: false, error: e.message || "network", offline: true };
  }
}

// --- Store hook --------------------------------------------------------------
function useStore() {
  const [data, setData] = React.useState(load);
  const [syncStatus, setSyncStatus] = React.useState("loading"); // loading | online | offline | syncing
  const [lastSyncAt, setLastSyncAt] = React.useState(null);
  const dataRef = React.useRef(data);

  React.useEffect(() => { save(data); dataRef.current = data; }, [data]);

  // --- Bootstrap on mount: try API, fall back to local cache
  React.useEffect(() => {
    let cancelled = false;
    (async () => {
      const r = await api("GET", "bootstrap");
      if (cancelled) return;
      if (!r?.ok) { setSyncStatus("offline"); return; }
      const serverData = r.data || {};
      const serverHasData = Object.values(serverData).some(arr => Array.isArray(arr) && arr.length > 0);
      if (serverHasData) {
        // Server is source of truth — overwrite local (keep settings client-side)
        setData(prev => migrate({ ...prev, ...serverData }));
        setSyncStatus("online");
        setLastSyncAt(new Date());
      } else {
        // Server vazio — empurra local pra cima (preserva dados locais)
        const snap = dataRef.current;
        const r2 = await api("POST", "sync", {
          people: snap.people, appointments: snap.appointments,
          tasks: snap.tasks, alerts: snap.alerts, notes: snap.notes,
        });
        if (cancelled) return;
        if (r2?.ok) { setSyncStatus("online"); setLastSyncAt(new Date()); }
        else setSyncStatus("offline");
      }
    })();
    return () => { cancelled = true; };
  }, []);

  // Fire API call without blocking UI; surface online/offline status
  const push = (method, path, body) => {
    api(method, path, body).then(r => {
      if (r?.offline) setSyncStatus("offline");
      else if (r?.ok) {
        setSyncStatus(s => s === "offline" ? "online" : s);
        setLastSyncAt(new Date());
      }
    });
  };

  const update = (fn) => setData((prev) => {
    const next = typeof fn === "function" ? fn(prev) : fn;
    return next;
  });

  // Build a status-change audit entry when patch changes status
  const patchWithAudit = (orig, patch) => {
    const entries = [];
    if (patch.status && patch.status !== orig.status) {
      entries.push(auditEntry("status", `${STATUS_META[orig.status]?.label || orig.status} → ${STATUS_META[patch.status]?.label || patch.status}`));
    }
    if (patch.date && patch.date !== orig.date) entries.push(auditEntry("data", `${orig.date} → ${patch.date}`));
    if (patch.time && patch.time !== orig.time) entries.push(auditEntry("horário", `${orig.time} → ${patch.time}`));
    if (patch.notes !== undefined && patch.notes !== orig.notes) entries.push(auditEntry("anotações", "atualizadas"));
    if (entries.length === 0) return patch;
    return { ...patch, audit: [...(orig.audit || []), ...entries] };
  };

  return {
    data,
    syncStatus,
    lastSyncAt,
    // appointments
    addAppointment: (a) => {
      const full = { ...a, id: uid(), audit: [auditEntry("criado")], price: a.price ?? (dataRef.current.settings.prices[a.type] || 0) };
      update((d) => ({ ...d, appointments: [...d.appointments, full] }));
      push("POST", "appointments", full);
    },
    addAppointments: (arr) => {
      const full = arr.map(a => ({ ...a, id: uid(), audit: [auditEntry("criado", "recorrência")], price: a.price ?? (dataRef.current.settings.prices[a.type] || 0) }));
      update((d) => ({ ...d, appointments: [...d.appointments, ...full] }));
      push("POST", "appointments", full);
    },
    updateAppointment: (id, patch) => {
      let merged = null;
      update((d) => ({
        ...d,
        appointments: d.appointments.map(a => {
          if (a.id !== id) return a;
          merged = { ...a, ...patchWithAudit(a, patch) };
          return merged;
        })
      }));
      if (merged) push("PATCH", "appointments/" + id, merged);
    },
    deleteAppointment: (id) => {
      update((d) => ({ ...d, appointments: d.appointments.filter(a => a.id !== id) }));
      push("DELETE", "appointments/" + id);
    },
    // people
    addPerson: (p) => {
      const full = { ...p, id: uid() };
      update((d) => ({ ...d, people: [...d.people, full] }));
      push("POST", "people", full);
    },
    addPeople: (arr) => {
      const full = arr.map(p => ({ ...p, id: uid() }));
      update((d) => ({ ...d, people: [...d.people, ...full] }));
      push("POST", "people", full);
    },
    updatePerson: (id, patch) => {
      let merged = null;
      update((d) => ({
        ...d,
        people: d.people.map(p => { if (p.id !== id) return p; merged = { ...p, ...patch }; return merged; })
      }));
      if (merged) push("PATCH", "people/" + id, merged);
    },
    deletePerson: (id) => {
      update((d) => ({ ...d, people: d.people.filter(p => p.id !== id) }));
      push("DELETE", "people/" + id);
    },
    // tasks
    addTask: (t) => {
      const full = { ...t, id: uid() };
      update((d) => ({ ...d, tasks: [...d.tasks, full] }));
      push("POST", "tasks", full);
    },
    updateTask: (id, patch) => {
      let merged = null;
      update((d) => ({
        ...d,
        tasks: d.tasks.map(t => { if (t.id !== id) return t; merged = { ...t, ...patch }; return merged; })
      }));
      if (merged) push("PATCH", "tasks/" + id, merged);
    },
    deleteTask: (id) => {
      update((d) => ({ ...d, tasks: d.tasks.filter(t => t.id !== id) }));
      push("DELETE", "tasks/" + id);
    },
    // alerts
    addAlert: (a) => {
      const full = { ...a, id: uid() };
      update((d) => ({ ...d, alerts: [...d.alerts, full] }));
      push("POST", "alerts", full);
    },
    deleteAlert: (id) => {
      update((d) => ({ ...d, alerts: d.alerts.filter(a => a.id !== id) }));
      push("DELETE", "alerts/" + id);
    },
    // notes
    addNote: (n) => {
      const full = { ...n, id: uid() };
      update((d) => ({ ...d, notes: [...d.notes, full] }));
      push("POST", "notes", full);
    },
    updateNote: (id, patch) => {
      let merged = null;
      update((d) => ({
        ...d,
        notes: d.notes.map(n => { if (n.id !== id) return n; merged = { ...n, ...patch }; return merged; })
      }));
      if (merged) push("PATCH", "notes/" + id, merged);
    },
    deleteNote: (id) => {
      update((d) => ({ ...d, notes: d.notes.filter(n => n.id !== id) }));
      push("DELETE", "notes/" + id);
    },
    // settings (client-only, no API)
    setTheme: (theme) => update((d) => ({ ...d, settings: { ...d.settings, theme } })),
    setAccent: (accent) => update((d) => ({ ...d, settings: { ...d.settings, accent } })),
    setPrice: (type, price) => update((d) => ({ ...d, settings: { ...d.settings, prices: { ...d.settings.prices, [type]: price } } })),
    markBackup: () => update((d) => ({ ...d, settings: { ...d.settings, lastBackup: new Date().toISOString() } })),
    markReminded: (apptId) => update((d) => ({ ...d, settings: { ...d.settings, notifiedReminders: [...(d.settings.notifiedReminders || []), apptId] } })),
    // sync manual
    syncNow: async () => {
      setSyncStatus("syncing");
      const snap = dataRef.current;
      const r = await api("POST", "sync", {
        people: snap.people, appointments: snap.appointments,
        tasks: snap.tasks, alerts: snap.alerts, notes: snap.notes,
      });
      if (r?.ok) { setSyncStatus("online"); setLastSyncAt(new Date()); }
      else setSyncStatus("offline");
    },
    pullRemote: async () => {
      setSyncStatus("syncing");
      const r = await api("GET", "bootstrap");
      if (r?.ok) {
        setData(prev => migrate({ ...prev, ...r.data }));
        setSyncStatus("online");
        setLastSyncAt(new Date());
      } else {
        setSyncStatus("offline");
      }
    },
    // import / export / reset
    exportJSON: () => {
      const blob = new Blob([JSON.stringify(data, null, 2)], { type: "application/json" });
      const url = URL.createObjectURL(blob);
      const a = document.createElement("a");
      a.href = url;
      a.download = `agenda-export-${new Date().toISOString().slice(0,10)}.json`;
      a.click();
      URL.revokeObjectURL(url);
      update((d) => ({ ...d, settings: { ...d.settings, lastBackup: new Date().toISOString() } }));
    },
    importJSON: (file) => {
      const reader = new FileReader();
      reader.onload = (e) => {
        try {
          const parsed = JSON.parse(e.target.result);
          const migrated = migrate(parsed);
          setData(migrated);
          // push pra nuvem
          api("POST", "sync", {
            people: migrated.people, appointments: migrated.appointments,
            tasks: migrated.tasks, alerts: migrated.alerts, notes: migrated.notes,
          }).then(r => { if (r?.ok) { setSyncStatus("online"); setLastSyncAt(new Date()); } });
        } catch (err) {
          alert("Arquivo JSON inválido.");
        }
      };
      reader.readAsText(file);
    },
    resetAll: () => {
      if (confirm("Apagar tudo e restaurar dados de exemplo? Isto também apaga os dados na nuvem.")) {
        const fresh = seedData();
        setData(fresh);
        api("POST", "sync", {
          people: fresh.people, appointments: fresh.appointments,
          tasks: fresh.tasks, alerts: fresh.alerts, notes: fresh.notes,
        });
      }
    },
  };
}

// --- Helpers -----------------------------------------------------------------
const TODAY_ISO = fmtISO(new Date());

function startOfWeek(d) {
  const date = new Date(d);
  const day = date.getDay();
  const diff = (day === 0 ? -6 : 1 - day);
  date.setDate(date.getDate() + diff);
  date.setHours(0,0,0,0);
  return date;
}

function weekDates(ref = new Date()) {
  const start = startOfWeek(ref);
  return Array.from({ length: 7 }, (_, i) => {
    const d = new Date(start);
    d.setDate(start.getDate() + i);
    return d;
  });
}

// Generate N recurring instances of an appointment
function expandRecurring(base, frequency, count) {
  // frequency: "weekly" | "biweekly" | "monthly"
  const arr = [];
  const start = new Date(base.date + "T00:00:00");
  for (let i = 0; i < count; i++) {
    const d = new Date(start);
    if (frequency === "weekly") d.setDate(start.getDate() + 7 * i);
    else if (frequency === "biweekly") d.setDate(start.getDate() + 14 * i);
    else if (frequency === "monthly") d.setMonth(start.getMonth() + i);
    arr.push({ ...base, date: fmtISO(d) });
  }
  return arr;
}

// Simple CSV parser for people import: name,phone,email,tag,source,note
function parseCSV(text) {
  const lines = text.trim().split(/\r?\n/);
  if (lines.length === 0) return [];
  const headers = lines[0].split(",").map(h => h.trim().toLowerCase());
  const map = { nome: "name", name: "name", telefone: "phone", whatsapp: "phone", phone: "phone", email: "email", tag: "tag", origem: "source", source: "source", nota: "note", note: "note", observacao: "note", "observação": "note" };
  return lines.slice(1).filter(Boolean).map(line => {
    const cells = line.split(",").map(c => c.trim());
    const obj = { name: "", phone: "", email: "", tag: "Lead quente", source: "Outro", note: "" };
    headers.forEach((h, i) => { const key = map[h]; if (key) obj[key] = cells[i] || obj[key]; });
    return obj;
  }).filter(p => p.name);
}

// --- Note templates ----------------------------------------------------------
const NOTE_TEMPLATES = {
  "primeira-sessao": {
    label: "Primeira sessão",
    body: "Contexto trazido pelo cliente:\n- \n\nDor principal:\n- \n\nObjetivo:\n- \n\nPróximos passos:\n- ",
  },
  "follow-up": {
    label: "Follow-up",
    body: "Status desde a última conversa:\n- \n\nAvanços percebidos:\n- \n\nPróxima ação:\n- ",
  },
  "sessao-fechamento": {
    label: "Fechamento",
    body: "Resultados alcançados:\n- \n\nO que ficou pendente:\n- \n\nDepoimento / feedback:\n- ",
  },
  "checkin-rapido": {
    label: "Check-in rápido",
    body: "Como está hoje:\n- \n\nUma coisa que vai resolver esta semana:\n- ",
  },
};

// --- Accent palettes ---------------------------------------------------------
const ACCENTS = {
  indigo: { name: "Índigo",  light: "#4f46e5", strong: "#4338ca", soft: "#818cf8" },
  blue:   { name: "Azul",    light: "#2563eb", strong: "#1d4ed8", soft: "#60a5fa" },
  green:  { name: "Verde",   light: "#16a34a", strong: "#15803d", soft: "#4ade80" },
  amber:  { name: "Âmbar",   light: "#d97706", strong: "#b45309", soft: "#fbbf24" },
  rose:   { name: "Rosa",    light: "#e11d48", strong: "#be123c", soft: "#fb7185" },
  violet: { name: "Violeta", light: "#7c3aed", strong: "#6d28d9", soft: "#c084fc" },
  slate:  { name: "Grafite", light: "#475569", strong: "#334155", soft: "#94a3b8" },
};

const STATUS_META = {
  agendado:   { label: "Agendado",   dot: "var(--c-blue)" },
  confirmado: { label: "Confirmado", dot: "var(--c-green)" },
  realizado:  { label: "Realizado",  dot: "var(--c-fg-soft)" },
  cancelado:  { label: "Cancelado",  dot: "var(--c-red)" },
  reagendar:  { label: "Reagendar",  dot: "var(--c-amber)" },
};

const TYPE_META = {
  Entrevista: { hue: "var(--c-indigo)" },
  Mentoria:   { hue: "var(--c-green)" },
  Consulta:   { hue: "var(--c-purple)" },
  Reunião:    { hue: "var(--c-amber)" },
  "Follow-up":{ hue: "var(--c-blue)" },
};

// --- WhatsApp message generator ---------------------------------------------
function buildWhatsAppLink(person, appt) {
  if (!person?.phone) return null;
  const phone = (person.phone || "").replace(/\D/g, "");
  const fullPhone = phone.startsWith("55") ? phone : "55" + phone;
  const date = new Date(appt.date + "T00:00:00");
  const dateStr = date.toLocaleDateString("pt-BR", { weekday: "long", day: "2-digit", month: "long" });
  const firstName = (person.name || "").split(" ")[0];
  const meet = (appt.location || "").includes("meet.google.com") ? appt.location : null;
  const lines = [
    `Olá ${firstName}, tudo bem?`,
    ``,
    `Confirmando nossa ${appt.type.toLowerCase()} (${appt.title}) no dia ${dateStr} às ${appt.time} (duração de ${appt.duration} min).`,
    meet ? `` : null,
    meet ? `Link: ${meet}` : null,
    ``,
    `Qualquer mudança, me avisa por aqui. 🙏`,
  ].filter(l => l !== null);
  const text = encodeURIComponent(lines.join("\n"));
  return `https://wa.me/${fullPhone}?text=${text}`;
}

Object.assign(window, {
  useStore, uid, fmtISO, TODAY_ISO, startOfWeek, weekDates,
  STATUS_META, TYPE_META, ACCENTS, NOTE_TEMPLATES,
  dayOffset, expandRecurring, parseCSV, buildWhatsAppLink,
});
