/* |-------------------------------------------------------------------------- | Agência PSI |-------------------------------------------------------------------------- | Criado e desenvolvido por Leonardo Nohama | | Tecnologia aplicada à escuta. | Estrutura para o cuidado. | | Arquivo: src/services/DocumentGenerate.service.js | Data: 2026 | Local: São Carlos/SP — Brasil |-------------------------------------------------------------------------- | © 2026 — Todos os direitos reservados |-------------------------------------------------------------------------- */ import { supabase } from '@/lib/supabase/client'; const BUCKET = 'generated-docs'; // ── Helpers ────────────────────────────────────────────────── async function getOwnerId() { const { data, error } = await supabase.auth.getUser(); if (error) throw error; const uid = data?.user?.id; if (!uid) throw new Error('Sessão inválida.'); return uid; } async function getActiveTenantId(uid) { const { data, error } = await supabase .from('tenant_members') .select('tenant_id') .eq('user_id', uid) .eq('status', 'active') .order('created_at', { ascending: false }) .limit(1) .single(); if (error) throw error; if (!data?.tenant_id) throw new Error('Tenant não encontrado.'); return data.tenant_id; } // ── Carregar dados para preenchimento ─────────────────────── /** * Busca dados do paciente para preencher variaveis do template. */ export async function loadPatientData(patientId) { const { data, error } = await supabase .from('patients') .select(` nome_completo, nome_social, cpf, data_nascimento, telefone, email_principal, endereco, numero, bairro, cidade, estado, cep `) .eq('id', patientId) .single(); if (error) throw error; const p = data; const endereco = [p.endereco, p.numero, p.bairro, p.cidade, p.estado] .filter(Boolean).join(', '); return { paciente_nome: p.nome_completo || '', paciente_nome_social: p.nome_social || '', paciente_cpf: p.cpf || '', paciente_data_nascimento: p.data_nascimento ? new Date(p.data_nascimento).toLocaleDateString('pt-BR') : '', paciente_telefone: p.telefone || '', paciente_email: p.email_principal || '', paciente_endereco: endereco }; } /** * Busca dados da sessao (agenda_evento) para preencher variaveis. */ export async function loadSessionData(agendaEventoId) { if (!agendaEventoId) return {}; const { data, error } = await supabase .from('agenda_eventos') .select('inicio_em, fim_em, modalidade, price') .eq('id', agendaEventoId) .single(); if (error) return {}; const s = data; const inicio = s.inicio_em ? new Date(s.inicio_em) : null; const fim = s.fim_em ? new Date(s.fim_em) : null; return { data_sessao: inicio ? inicio.toLocaleDateString('pt-BR') : '', hora_inicio: inicio ? inicio.toLocaleTimeString('pt-BR', { hour: '2-digit', minute: '2-digit' }) : '', hora_fim: fim ? fim.toLocaleTimeString('pt-BR', { hour: '2-digit', minute: '2-digit' }) : '', modalidade: s.modalidade || '', valor: s.price ? `R$ ${Number(s.price).toFixed(2).replace('.', ',')}` : '' }; } /** * Busca dados do terapeuta (profile + tenant_member). */ export async function loadTherapistData() { const ownerId = await getOwnerId(); const { data: profile } = await supabase .from('profiles') .select('full_name, phone') .eq('id', ownerId) .single(); // Email vem de auth.users (nao existe em profiles) const { data: userData } = await supabase.auth.getUser(); const email = userData?.user?.email || ''; return { terapeuta_nome: profile?.full_name || '', terapeuta_crp: '', // CRP ainda nao existe no banco — preencher manualmente terapeuta_email: email, terapeuta_telefone: profile?.phone || '' }; } /** * Busca dados da clinica (tenant). */ export async function loadClinicData(tenantId) { // Usa select('*') pois campos de endereço (logradouro, numero, etc.) // dependem da migration 003_tenants_address_fields ter sido aplicada const { data: tenant } = await supabase .from('tenants') .select('*') .eq('id', tenantId) .maybeSingle(); if (!tenant) { return { clinica_nome: '', clinica_endereco: '', clinica_telefone: '', clinica_cnpj: '' }; } // Usa campos estruturados se disponiveis, senao cai no address texto livre const endereco = tenant.logradouro ? [tenant.logradouro, tenant.numero, tenant.bairro, tenant.cidade, tenant.estado] .filter(Boolean).join(', ') : tenant.address || ''; return { clinica_nome: tenant.name || '', clinica_endereco: endereco, clinica_telefone: tenant.phone || '', clinica_cnpj: '' }; } // ── Montar dados gerais ───────────────────────────────────── function getDateVariables() { const now = new Date(); const meses = [ 'janeiro', 'fevereiro', 'março', 'abril', 'maio', 'junho', 'julho', 'agosto', 'setembro', 'outubro', 'novembro', 'dezembro' ]; return { data_atual: now.toLocaleDateString('pt-BR'), data_atual_extenso: `${now.getDate()} de ${meses[now.getMonth()]} de ${now.getFullYear()}` }; } /** * Carrega todos os dados necessarios para preencher um template. */ export async function loadAllVariables(patientId, agendaEventoId = null) { const ownerId = await getOwnerId(); const tenantId = await getActiveTenantId(ownerId); const [patient, session, therapist, clinic] = await Promise.all([ loadPatientData(patientId), loadSessionData(agendaEventoId), loadTherapistData(), loadClinicData(tenantId) ]); return { ...patient, ...session, ...therapist, ...clinic, ...getDateVariables(), cidade_estado: clinic.clinica_endereco ? `${clinic.clinica_endereco.split(', ').slice(-2).join('/')}` : '' }; } // ── Preencher template ────────────────────────────────────── /** * Substitui {{variavel}} no HTML pelos valores fornecidos. */ export function fillTemplate(html, variables = {}) { return String(html || '').replace(/\{\{(\w+)\}\}/g, (match, key) => { return variables[key] !== undefined ? String(variables[key]) : match; }); } /** * Monta o HTML completo do documento (cabecalho + corpo + rodape). */ export function buildFullHtml(template, variables = {}) { const cabecalho = fillTemplate(template.cabecalho_html || '', variables); const corpo = fillTemplate(template.corpo_html || '', variables); const rodape = fillTemplate(template.rodape_html || '', variables); return `