import { Request, Response } from 'express'; import { query } from '../config/database.js'; // Send a message export const sendMessage = async (req: Request, res: Response) => { try { const { toUserId, message } = req.body; const fromUserId = req.appUser?.externalId; const organizationId = req.headers['x-organization-id'] as string; if (!organizationId) { return res.status(400).json({ error: 'Organização não selecionada.' }); } if (!fromUserId) { return res.status(401).json({ error: 'Usuário não autenticado.' }); } if (!toUserId || !message) { return res.status(400).json({ error: 'Destinatário e mensagem são obrigatórios.' }); } // Check if there's already a pending (unread) message from this user to that user const existingRes = await query( 'SELECT * FROM gpi.messages WHERE organization_id = $1 AND from_user_id = $2 AND to_user_id = $3 AND is_read = false', [organizationId, fromUserId, toUserId] ); if (existingRes.rows.length > 0) { const updatedRes = await query( 'UPDATE gpi.messages SET message = $1, updated_at = NOW() WHERE id = $2 RETURNING *', [message, existingRes.rows[0].id] ); return res.json(updatedRes.rows[0]); } const insertRes = await query( `INSERT INTO gpi.messages (organization_id, from_user_id, to_user_id, message) VALUES ($1, $2, $3, $4) RETURNING *`, [organizationId, fromUserId, toUserId, message] ); res.status(201).json(insertRes.rows[0]); } catch (error) { console.error('Error sending message:', error); res.status(500).json({ error: 'Erro ao enviar mensagem.' }); } }; // Get unread messages for current user export const getUnreadMessages = async (req: Request, res: Response) => { try { const toUserId = req.appUser?.externalId; const organizationId = req.headers['x-organization-id'] as string; if (!organizationId || !toUserId) { return res.status(400).json({ error: 'Contexto incompleto.' }); } const sql = ` SELECT m.*, u.name as "fromUserName", u.email as "fromUserEmail" FROM gpi.messages m LEFT JOIN gpi.users u ON m.from_user_id = u.clerk_id OR m.from_user_id = u.logto_id WHERE m.organization_id = $1 AND m.to_user_id = $2 AND m.is_read = false AND m.is_archived = false AND m.is_deleted_by_recipient = false ORDER BY m.created_at DESC `; const result = await query(sql, [organizationId, toUserId]); const messages = result.rows.map(m => ({ ...m, fromUser: { name: m.fromUserName, email: m.fromUserEmail } })); res.json(messages); } catch (error) { console.error('Error getting unread messages:', error); res.status(500).json({ error: 'Erro ao buscar mensagens.' }); } }; // Mark message as read export const markMessageAsRead = async (req: Request, res: Response) => { try { const { id } = req.params; const userId = req.appUser?.externalId; const organizationId = req.headers['x-organization-id'] as string; const result = await query( 'UPDATE gpi.messages SET is_read = true, read_at = NOW() WHERE id = $1 AND organization_id = $2 AND to_user_id = $3 RETURNING *', [id, organizationId, userId] ); if (result.rows.length === 0) { return res.status(404).json({ error: 'Mensagem não encontrada.' }); } res.json(result.rows[0]); } catch (error) { console.error('Error marking message as read:', error); res.status(500).json({ error: 'Erro ao marcar mensagem como lida.' }); } }; // Get my pending (unread) sent messages export const getMyPendingMessages = async (req: Request, res: Response) => { try { const fromUserId = req.appUser?.externalId; const organizationId = req.headers['x-organization-id'] as string; if (!organizationId || !fromUserId) { return res.status(400).json({ error: 'Contexto incompleto.' }); } const sql = ` SELECT m.*, u.name as "toUserName", u.email as "toUserEmail" FROM gpi.messages m LEFT JOIN gpi.users u ON m.to_user_id = u.clerk_id OR m.to_user_id = u.logto_id WHERE m.organization_id = $1 AND m.from_user_id = $2 AND m.is_read = false ORDER BY m.created_at DESC `; const result = await query(sql, [organizationId, fromUserId]); const messages = result.rows.map(m => ({ ...m, toUser: { name: m.toUserName, email: m.toUserEmail } })); res.json(messages); } catch (error) { console.error('Error getting pending messages:', error); res.status(500).json({ error: 'Erro ao buscar mensagens pendentes.' }); } }; // Delete a message (only if unread and sender is the current user) export const deleteMessage = async (req: Request, res: Response) => { try { const { id } = req.params; const userId = req.appUser?.externalId; const organizationId = req.headers['x-organization-id'] as string; const result = await query( 'DELETE FROM gpi.messages WHERE id = $1 AND from_user_id = $2 AND organization_id = $3 AND is_read = false', [id, userId, organizationId] ); if (result.rowCount === 0) { return res.status(404).json({ error: 'Mensagem não encontrada ou já lida.' }); } res.json({ message: 'Mensagem deletada com sucesso.' }); } catch (error) { console.error('Error deleting message:', error); res.status(500).json({ error: 'Erro ao deletar mensagem.' }); } }; // Recipient archives a message export const archiveMessage = async (req: Request, res: Response) => { try { const { id } = req.params; const userId = req.appUser?.externalId; const organizationId = req.headers['x-organization-id'] as string; const result = await query( 'UPDATE gpi.messages SET is_archived = true, is_read = true WHERE id = $1 AND to_user_id = $2 AND organization_id = $3 RETURNING *', [id, userId, organizationId] ); if (result.rows.length === 0) return res.status(404).json({ error: 'Mensagem não encontrada.' }); res.json(result.rows[0]); } catch (error) { console.error('Error archiving message:', error); res.status(500).json({ error: 'Erro ao arquivar mensagem.' }); } }; export const recipientDeleteMessage = async (req: Request, res: Response) => { try { const { id } = req.params; const userId = req.appUser?.externalId; const organizationId = req.headers['x-organization-id'] as string; const result = await query( 'UPDATE gpi.messages SET is_deleted_by_recipient = true WHERE id = $1 AND to_user_id = $2 AND organization_id = $3 RETURNING *', [id, userId, organizationId] ); if (result.rows.length === 0) return res.status(404).json({ error: 'Mensagem não encontrada.' }); res.json({ message: 'Mensagem excluída com sucesso.' }); } catch (error) { console.error('Error deleting message:', error); res.status(500).json({ error: 'Erro ao excluir mensagem.' }); } };