import { serve } from "https://deno.land/std@0.168.0/http/server.ts" import { createClient } from "https://esm.sh/@supabase/supabase-js@2" import { JWT } from "https://esm.sh/google-auth-library@8.9.0" const corsHeaders = { 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Headers': 'authorization, x-client-info, apiKey, content-type', } serve(async (req) => { if (req.method === 'OPTIONS') return new Response('ok', { headers: corsHeaders }) try { const bodyText = await req.text(); const payload = JSON.parse(bodyText); // Estraggo i dati dal payload standard di Supabase const tableName = payload.table; const record = payload.record; if (!tableName || !record) { throw new Error("Payload non valido, manca table o record."); } let event_type = ''; let target_staff_id = ''; let title = ''; let description = ''; let reference_id = ''; // Inizializziamo il client Supabase subito, ci serve per le query const supabaseClient = createClient( Deno.env.get('SUPABASE_URL') ?? '', Deno.env.get('SUPABASE_SERVICE_ROLE_KEY') ?? '' ) // SMISTAMENTO IN BASE ALLA TABELLA if (tableName === 'task_assignments') { event_type = 'task_assigned'; target_staff_id = record.staff_id; reference_id = record.task_id; title = 'Nuovo Task Assegnato'; // 1. Peschiamo i dettagli completi del task const { data: taskData } = await supabaseClient .from('tasks') .select('*') .eq('id', reference_id) .single(); // 2. Peschiamo il nome del creatore let creatorName = "Admin"; if (taskData?.created_by_id) { const { data: creatorData } = await supabaseClient .from('staff_members') .select('first_name, last_name') .eq('id', taskData.created_by_id) .single(); if (creatorData) { creatorName = `${creatorData.first_name} ${creatorData.last_name}`.trim(); } } // 3. Formattiamo la data (se esiste) let dueDateStr = 'Nessuna scadenza'; if (taskData?.due_date) { const d = new Date(taskData.due_date); dueDateStr = d.toLocaleDateString('it-IT'); } // 4. Costruiamo il Body multilinea per Android const taskTitle = taskData?.title || 'Senza titolo'; const taskDesc = taskData?.description || 'Nessuna descrizione fornita.'; description = `${taskTitle}\n\nCreato da: ${creatorName}\nScadenza: ${dueDateStr}\nDettagli: ${taskDesc}`; } // 1. Leggiamo le preferenze specifiche di questo dipendente const { data: settings, error: settingsError } = await supabaseClient .from('staff_notification_settings') .select('*') .eq('staff_id', target_staff_id) .single() if (settingsError || !settings) throw new Error('Preferenze utente non trovate') // 2. Determiniamo QUALI canali usare in base all'evento e agli switch dell'utente let sendPush = false let sendEmail = false switch (event_type) { case 'task_assigned': sendPush = settings.task_assigned_push sendEmail = settings.task_assigned_email break case 'note_invited': sendPush = settings.note_invited_push sendEmail = settings.note_invited_email break case 'new_operation': sendPush = settings.new_operation_push sendEmail = settings.new_operation_email break case 'new_ticket': sendPush = settings.new_ticket_push sendEmail = settings.new_ticket_email break default: throw new Error('Tipo evento non riconosciuto') } // Se l'utente ha spento tutto, interrompiamo subito risparmiando risorse if (!sendPush && !sendEmail) { return new Response(JSON.stringify({ message: 'L\'utente ha disattivato le notifiche per questo evento.' }), { headers: { ...corsHeaders, 'Content-Type': 'application/json' }, status: 200 }) } // Se arriviamo qui, dobbiamo inviare qualcosa. Prepariamo i dati dell'utente. const { data: staffMember } = await supabaseClient .from('staff_members') .select('email, first_name') .eq('id', target_staff_id) .single() // 3. LOGICA PUSH (FCM) if (sendPush) { const firebaseSecret = Deno.env.get('FIREBASE_SERVICE_ACCOUNT'); if (!firebaseSecret) { console.error("ERRORE: Secret FIREBASE_SERVICE_ACCOUNT mancante nel progetto!"); } else { const credentials = JSON.parse(firebaseSecret); const jwtClient = new JWT({ email: credentials.client_email, key: credentials.private_key, scopes: ['https://www.googleapis.com/auth/firebase.messaging'], }); const fcmAccessToken = (await jwtClient.getAccessToken()).token; const { data: devices } = await supabaseClient .from('staff_devices') .select('fcm_token') .eq('staff_id', target_staff_id); if (devices && devices.length > 0) { for (const device of devices) { try { const res = await fetch(`https://fcm.googleapis.com/v1/projects/${credentials.project_id}/messages:send`, { method: 'POST', headers: { 'Authorization': `Bearer ${fcmAccessToken}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ message: { token: device.fcm_token, notification: { title, body: description }, data: { click_action: 'FLUTTER_NOTIFICATION_CLICK', eventType: event_type, referenceId: reference_id }, }, }), }); // QUI È DOVE CATTURIAMO LA RISPOSTA DI GOOGLE const fcmResponseData = await res.json(); if (!res.ok) { console.error("FCM HA RIFIUTATO LA NOTIFICA:", fcmResponseData); } } catch (err) { console.error('Errore di rete durante invio Push:', err); } } } } } // 4. LOGICA EMAIL (Resend) if (sendEmail && staffMember?.email) { const resendApiKey = Deno.env.get('RESEND_API_KEY') if (resendApiKey) { try { await fetch('https://api.resend.com/emails', { method: 'POST', headers: { 'Authorization': `Bearer ${resendApiKey}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ from: 'FLUX Notifiche ', to: staffMember.email, subject: title, html: `

Ciao ${staffMember.first_name},

${description}


Il team FLUX

`, }), }) } catch (err) { console.error('Errore invio Email:', err) } } } return new Response(JSON.stringify({ success: true, push_sent: sendPush, email_sent: sendEmail }), { headers: { ...corsHeaders, 'Content-Type': 'application/json' }, status: 200 }) } catch (error) { console.error("ERRORE FATALE NELLA FUNZIONE:", error); return new Response(JSON.stringify({ error: error.message }), { headers: { ...corsHeaders, 'Content-Type': 'application/json' }, status: 500 }) } })