import { supabase } from './supabase'; import { Campaign, Proposition, Participant, Vote, ParticipantWithVoteStatus, Setting, SmtpSettings } from '@/types'; import { encryptionService } from './encryption'; import { emailService } from './email'; // Services pour les campagnes export const campaignService = { async getAll(): Promise { const { data, error } = await supabase .from('campaigns') .select('*') .order('created_at', { ascending: false }); if (error) throw error; return data || []; }, // eslint-disable-next-line @typescript-eslint/no-explicit-any async create(campaign: any): Promise { const { data, error } = await supabase .from('campaigns') .insert(campaign) .select() .single(); if (error) throw error; return data; }, // eslint-disable-next-line @typescript-eslint/no-explicit-any async update(id: string, updates: any): Promise { const { data, error } = await supabase .from('campaigns') .update(updates) .eq('id', id) .select() .single(); if (error) throw error; return data; }, async delete(id: string): Promise { console.log('Tentative de suppression de la campagne:', id); // La suppression en cascade est gérée par la base de données // grâce à ON DELETE CASCADE dans les contraintes de clés étrangères const { error } = await supabase .from('campaigns') .delete() .eq('id', id); if (error) { console.error('Erreur lors de la suppression:', error); throw error; } console.log('Campagne supprimée avec succès'); }, async getStats(campaignId: string): Promise<{ propositions: number; participants: number }> { const [propositionsResult, participantsResult] = await Promise.all([ supabase .from('propositions') .select('id', { count: 'exact', head: true }) .eq('campaign_id', campaignId), supabase .from('participants') .select('id', { count: 'exact', head: true }) .eq('campaign_id', campaignId) ]); if (propositionsResult.error) throw propositionsResult.error; if (participantsResult.error) throw participantsResult.error; return { propositions: propositionsResult.count || 0, participants: participantsResult.count || 0 }; } }; // Services pour les propositions export const propositionService = { async getByCampaign(campaignId: string): Promise { const { data, error } = await supabase .from('propositions') .select('*') .eq('campaign_id', campaignId) .order('created_at', { ascending: false }); if (error) throw error; return data || []; }, // eslint-disable-next-line @typescript-eslint/no-explicit-any async create(proposition: any): Promise { const { data, error } = await supabase .from('propositions') .insert(proposition) .select() .single(); if (error) throw error; return data; }, // eslint-disable-next-line @typescript-eslint/no-explicit-any async update(id: string, updates: any): Promise { try { // Effectuer la mise à jour directement const { data, error } = await supabase .from('propositions') .update(updates) .eq('id', id) .select('*') .single(); if (error) { console.error('Erreur Supabase lors de la mise à jour:', error); if (error.code === 'PGRST116') { throw new Error(`Proposition avec l'ID ${id} non trouvée`); } throw new Error(`Erreur lors de la mise à jour: ${error.message || 'Erreur inconnue'}`); } if (!data) { throw new Error('Aucune donnée retournée après la mise à jour'); } return data; } catch (error: any) { console.error('Erreur dans propositionService.update:', error); throw error; } }, async delete(id: string): Promise { const { error } = await supabase .from('propositions') .delete() .eq('id', id); if (error) throw error; } }; // Services pour les participants export const participantService = { async getByCampaign(campaignId: string): Promise { const { data, error } = await supabase .from('participants') .select('*') .eq('campaign_id', campaignId) .order('created_at', { ascending: false }); if (error) throw error; return data || []; }, // eslint-disable-next-line @typescript-eslint/no-explicit-any async create(participant: any): Promise { const { data, error } = await supabase .from('participants') .insert(participant) .select() .single(); if (error) throw error; return data; }, // eslint-disable-next-line @typescript-eslint/no-explicit-any async update(id: string, updates: any): Promise { try { // Effectuer la mise à jour directement const { data, error } = await supabase .from('participants') .update(updates) .eq('id', id) .select('*') .single(); if (error) { console.error('Erreur Supabase lors de la mise à jour du participant:', error); if (error.code === 'PGRST116') { throw new Error(`Participant avec l'ID ${id} non trouvé`); } throw new Error(`Erreur lors de la mise à jour du participant: ${error.message || 'Erreur inconnue'}`); } if (!data) { throw new Error('Aucune donnée retournée après la mise à jour du participant'); } return data; } catch (error: any) { console.error('Erreur dans participantService.update:', error); throw error; } }, async delete(id: string): Promise { const { error } = await supabase .from('participants') .delete() .eq('id', id); if (error) throw error; } }; // Services pour les votes export const voteService = { async getByParticipant(campaignId: string, participantId: string): Promise { const { data, error } = await supabase .from('votes') .select('*') .eq('campaign_id', campaignId) .eq('participant_id', participantId); if (error) throw error; return data || []; }, async getByProposition(propositionId: string): Promise { const { data, error } = await supabase .from('votes') .select('*') .eq('proposition_id', propositionId); if (error) throw error; return data || []; }, // eslint-disable-next-line @typescript-eslint/no-explicit-any async create(vote: any): Promise { const { data, error } = await supabase .from('votes') .insert(vote) .select() .single(); if (error) throw error; return data; }, // eslint-disable-next-line @typescript-eslint/no-explicit-any async update(id: string, updates: any): Promise { const { data, error } = await supabase .from('votes') .update(updates) .eq('id', id) .select() .single(); if (error) throw error; return data; }, async upsert(vote: { campaign_id: string; participant_id: string; proposition_id: string; amount: number }): Promise { const { data, error } = await supabase .from('votes') .upsert(vote, { onConflict: 'participant_id,proposition_id' }) .select() .single(); if (error) throw error; return data; }, async delete(id: string): Promise { const { error } = await supabase .from('votes') .delete() .eq('id', id); if (error) throw error; }, async getByCampaign(campaignId: string): Promise { const { data, error } = await supabase .from('votes') .select('*') .eq('campaign_id', campaignId); if (error) throw error; return data || []; }, async getParticipantVoteStatus(campaignId: string): Promise { const { data: participants, error: participantsError } = await supabase .from('participants') .select('*') .eq('campaign_id', campaignId); if (participantsError) throw participantsError; const { data: votes, error: votesError } = await supabase .from('votes') .select('*') .eq('campaign_id', campaignId); if (votesError) throw votesError; return participants.map(participant => { const participantVotes = votes.filter(vote => vote.participant_id === participant.id); const totalVotedAmount = participantVotes.reduce((sum, vote) => sum + vote.amount, 0); return { ...participant, has_voted: participantVotes.length > 0, total_voted_amount: totalVotedAmount }; }); } }; // Services pour les paramètres export const settingsService = { async getAll(): Promise { const { data, error } = await supabase .from('settings') .select('*') .order('category', { ascending: true }) .order('key', { ascending: true }); if (error) throw error; return data || []; }, async getByCategory(category: string): Promise { const { data, error } = await supabase .from('settings') .select('*') .eq('category', category) .order('key', { ascending: true }); if (error) throw error; return data || []; }, async getByKey(key: string): Promise { const { data, error } = await supabase .from('settings') .select('*') .eq('key', key) .single(); if (error) { if (error.code === 'PGRST116') return null; // No rows returned throw error; } return data; }, async getValue(key: string, defaultValue: string = ''): Promise { const setting = await this.getByKey(key); return setting?.value || defaultValue; }, async getBooleanValue(key: string, defaultValue: boolean = false): Promise { const value = await this.getValue(key, defaultValue.toString()); return value === 'true'; }, // eslint-disable-next-line @typescript-eslint/no-explicit-any async create(setting: any): Promise { const { data, error } = await supabase .from('settings') .insert(setting) .select() .single(); if (error) throw error; return data; }, // eslint-disable-next-line @typescript-eslint/no-explicit-any async update(key: string, updates: any): Promise { const { data, error } = await supabase .from('settings') .update(updates) .eq('key', key) .select() .single(); if (error) throw error; return data; }, async setValue(key: string, value: string): Promise { const existing = await this.getByKey(key); if (existing) { return this.update(key, { value }); } else { return this.create({ key, value, category: 'general' }); } }, async setBooleanValue(key: string, value: boolean): Promise { return this.setValue(key, value.toString()); }, async delete(key: string): Promise { const { error } = await supabase .from('settings') .delete() .eq('key', key); if (error) throw error; }, // Méthodes spécifiques pour les paramètres SMTP async getSmtpSettings(): Promise { const smtpKeys = [ 'smtp_host', 'smtp_port', 'smtp_username', 'smtp_password', 'smtp_secure', 'smtp_from_email', 'smtp_from_name' ]; const settings = await Promise.all( smtpKeys.map(key => this.getByKey(key)) ); return { host: settings[0]?.value || '', port: parseInt(settings[1]?.value || '587'), username: settings[2]?.value || '', password: encryptionService.isEncrypted(settings[3]?.value || '') ? encryptionService.decrypt(settings[3]?.value || '') : settings[3]?.value || '', secure: settings[4]?.value === 'true', from_email: settings[5]?.value || '', from_name: settings[6]?.value || '' }; }, async setSmtpSettings(smtpSettings: SmtpSettings): Promise { const settingsToUpdate = [ { key: 'smtp_host', value: smtpSettings.host, category: 'email', description: 'Serveur SMTP' }, { key: 'smtp_port', value: smtpSettings.port.toString(), category: 'email', description: 'Port SMTP' }, { key: 'smtp_username', value: smtpSettings.username, category: 'email', description: 'Nom d\'utilisateur SMTP' }, { key: 'smtp_password', value: encryptionService.encrypt(smtpSettings.password), category: 'email', description: 'Mot de passe SMTP (chiffré)' }, { key: 'smtp_secure', value: smtpSettings.secure.toString(), category: 'email', description: 'Connexion sécurisée SSL/TLS' }, { key: 'smtp_from_email', value: smtpSettings.from_email, category: 'email', description: 'Adresse email d\'expédition' }, { key: 'smtp_from_name', value: smtpSettings.from_name, category: 'email', description: 'Nom d\'expédition' } ]; await Promise.all( settingsToUpdate.map(setting => this.setValue(setting.key, setting.value)) ); }, async testSmtpConnection(smtpSettings: SmtpSettings): Promise<{ success: boolean; error?: string }> { try { // Validation basique des paramètres if (!smtpSettings.host || !smtpSettings.port || !smtpSettings.username || !smtpSettings.password) { return { success: false, error: 'Paramètres SMTP incomplets' }; } if (smtpSettings.port < 1 || smtpSettings.port > 65535) { return { success: false, error: 'Port SMTP invalide' }; } if (!smtpSettings.from_email.includes('@')) { return { success: false, error: 'Adresse email d\'expédition invalide' }; } // Test de connexion via API route return await emailService.testConnection(smtpSettings); } catch (error) { return { success: false, error: error instanceof Error ? error.message : 'Erreur inconnue' }; } }, async sendTestEmail(smtpSettings: SmtpSettings, toEmail: string): Promise<{ success: boolean; error?: string; messageId?: string }> { try { // Validation de l'email de destination if (!emailService.validateEmail(toEmail)) { return { success: false, error: 'Adresse email de destination invalide' }; } // Envoi de l'email de test via API route return await emailService.sendTestEmail(smtpSettings, toEmail); } catch (error) { return { success: false, error: error instanceof Error ? error.message : 'Erreur lors de l\'envoi de l\'email de test' }; } } };