// apiService.ts (V4) import { AuthResponse, UserProfile, Patient, Doctor, Appointment, NewAppointmentPayload, AvailableSlot, Report, ReportInput, DoctorAvailability, DoctorException, NetworkError, LoginResponse, SendMagicLinkResponse, LogoutResponse, GetCurrentUserResponse, RequestPasswordResetResponse, CreateUserWithPasswordResponse, HardDeleteUserResponse, RegisterPatientResponse, GetAvailableSlotsResponse, CreateAppointmentResponse, CancelAppointmentResponse, CreateReportResponse, UpdateReportResponse, ListResponse } from './types'; // Ação Futura: Mover estas chaves para variáveis de ambiente. const BASE_URL = process.env.NEXT_PUBLIC_SUPABASE_URL || 'https://yuanqfswhberkoevtmfr.supabase.co'; const API_KEY = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY || 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inl1YW5xZnN3aGJlcmtvZXZ0bWZyIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTQ5NTQzNjksImV4cCI6MjA3MDUzMDM2OX0.g8Fm4XAvtX46zifBZnYVH4tVuQkqUH6Ia9CXQj4DztQ'; /** * Cliente de API base que retorna uma união discriminada para todos os cenários. */ async function apiClient(endpoint: string, options: RequestInit = {}, isPublic: boolean = false): Promise<{ status: number; data: T } | NetworkError> { const headers = new Headers(options.headers || {}); headers.set('apikey', API_KEY); headers.set('Content-Type', 'application/json'); if (!isPublic) { try { const authSession = localStorage.getItem('supabase.auth.token'); if (authSession) { const session = JSON.parse(authSession); // A estrutura do token pode variar, ajuste se necessário const token = session?.currentSession?.access_token || session?.access_token; if (token) { headers.set('Authorization', `Bearer ${token}`); } } } catch (error) { console.error("Falha ao ler o token de autenticação do localStorage.", error); } } const config: RequestInit = { ...options, headers }; try { const response = await fetch(`${BASE_URL}${endpoint}`, config); if (response.status === 204) { return { status: response.status, data: undefined as T }; } const data = await response.json().catch(() => ({})); return { status: response.status, data: data as T, }; } catch (error) { if (error instanceof Error) { return { status: 'network_error', error }; } return { status: 'network_error', error: new Error('Erro de rede desconhecido') }; } } // V4 CHANGE: Adicionamos uma asserção de tipo `as Promise<...>` em cada função. // Isso informa ao TypeScript que confiamos que o retorno do apiClient corresponderá // à união discriminada específica que definimos para cada endpoint. // --- SERVIÇOS DE AUTENTICAÇÃO --- export const authService = { login: (credentials: { email: string; password: string }): Promise => { return apiClient('/auth/v1/token?grant_type=password', { method: 'POST', body: JSON.stringify(credentials), }, true) as Promise; }, sendMagicLink: (email: string): Promise => { return apiClient('/auth/v1/otp', { method: 'POST', body: JSON.stringify({ email }), }, true) as Promise; }, logout: (): Promise => { return apiClient('/auth/v1/logout', { method: 'POST' }) as Promise; }, getCurrentUser: (): Promise => { return apiClient('/functions/v1/user-info', { method: 'POST' }) as Promise; }, }; // --- SERVIÇOS DE USUÁRIOS --- export const userService = { requestPasswordReset: (email: string, redirectUrl?: string): Promise => { return apiClient('/request-password-reset', { method: 'POST', body: JSON.stringify({ email, redirect_url: redirectUrl }), }, true) as Promise; }, createUserWithPassword: (payload: object): Promise => { return apiClient('/create-user-with-password', { method: 'POST', body: JSON.stringify(payload), }) as Promise; }, hardDeleteUser_DANGEROUS: (userId: string): Promise => { return apiClient('/delete-user', { method: 'POST', body: JSON.stringify({ userId }), }) as Promise; }, deactivateUser: (userId: string): Promise => { return apiClient(`/rest/v1/profiles?id=eq.${userId}`, { method: 'PATCH', headers: { Prefer: 'return=representation' }, body: JSON.stringify({ disabled: true }), }); } }; // --- SERVIÇOS DE PACIENTES --- export const patientService = { registerPatient: (payload: { email: string; full_name: string; phone_mobile: string; cpf: string; birth_date?: string }): Promise => { return apiClient('/functions/v1/register-patient', { method: 'POST', body: JSON.stringify(payload), }, true) as Promise; }, list: (filters: { fullName?: string; cpf?: string; limit?: number; offset?: number } = {}): Promise> => { const query = new URLSearchParams(); if (filters.fullName) query.set('full_name', `ilike.*${filters.fullName}*`); if (filters.cpf) query.set('cpf', `eq.${filters.cpf}`); if (filters.limit) query.set('limit', String(filters.limit)); if (filters.offset) query.set('offset', String(filters.offset)); return apiClient(`/rest/v1/patients?${query.toString()}`) as Promise>; }, create: (payload: Omit): Promise => { return apiClient('/rest/v1/patients', { method: 'POST', headers: { Prefer: 'return=representation' }, body: JSON.stringify(payload), }); }, }; // --- SERVIÇOS DE MÉDICOS --- export const doctorService = { list: (filters: { specialty?: string; active?: boolean } = {}): Promise> => { const query = new URLSearchParams({ select: '*' }); if (filters.specialty) query.set('specialty', `eq.${filters.specialty}`); if (filters.active !== undefined) query.set('active', `eq.${filters.active}`); return apiClient(`/rest/v1/doctors?${query.toString()}`) as Promise>; }, }; // --- SERVIÇOS DE AGENDAMENTO E DISPONIBILIDADE --- export const scheduleService = { getAvailableSlots: (doctorId: string, date: string): Promise => { return apiClient('/functions/v1/get-available-slots', { method: 'POST', body: JSON.stringify({ doctor_id: doctorId, date }), }) as Promise; }, createAppointment: (payload: NewAppointmentPayload): Promise => { return apiClient('/rest/v1/appointments', { method: 'POST', headers: { Prefer: 'return=representation' }, body: JSON.stringify(payload), }) as Promise; }, listAppointments: (filters: { doctorId?: string; patientId?: string; status?: string }): Promise> => { const query = new URLSearchParams(); if (filters.doctorId) query.set('doctor_id', `eq.${filters.doctorId}`); if (filters.patientId) query.set('patient_id', `eq.${filters.patientId}`); if (filters.status) query.set('status', `eq.${filters.status}`); return apiClient(`/rest/v1/appointments?${query.toString()}`) as Promise>; }, cancelAppointment: (appointmentId: string, reason: string): Promise => { return apiClient(`/rest/v1/appointments?id=eq.${appointmentId}`, { method: 'PATCH', headers: { Prefer: 'return=representation' }, body: JSON.stringify({ status: 'cancelled', cancellation_reason: reason, cancelled_at: new Date().toISOString(), }), }) as Promise; }, listAvailability: (filters: { doctorId?: string } = {}): Promise> => { const query = new URLSearchParams(); if (filters.doctorId) query.set('doctor_id', `eq.${filters.doctorId}`); return apiClient(`/rest/v1/doctor_availability?${query.toString()}`) as Promise>; }, listExceptions: (filters: { doctorId?: string; date?: string } = {}): Promise> => { const query = new URLSearchParams(); if (filters.doctorId) query.set('doctor_id', `eq.${filters.doctorId}`); if (filters.date) query.set('date', `eq.${filters.date}`); return apiClient(`/rest/v1/doctor_exceptions?${query.toString()}`) as Promise>; }, }; // --- SERVIÇOS DE LAUDOS (REPORTS) --- export const reportService = { list: (filters: { patientId?: string; createdBy?: string }): Promise> => { const query = new URLSearchParams({ order: 'created_at.desc' }); if (filters.patientId) query.set('patient_id', `eq.${filters.patientId}`); if (filters.createdBy) query.set('created_by', `eq.${filters.createdBy}`); return apiClient(`/rest/v1/reports?${query.toString()}`) as Promise>; }, create: (payload: ReportInput): Promise => { return apiClient('/rest/v1/reports', { method: 'POST', headers: { Prefer: 'return=representation' }, body: JSON.stringify(payload), }) as Promise; }, update: (reportId: string, payload: Partial): Promise => { return apiClient(`/rest/v1/reports?id=eq.${reportId}`, { method: 'PATCH', headers: { Prefer: 'return=representation' }, body: JSON.stringify(payload), }) as Promise; }, };