import { getPool } from "./database"; import { UserProfile } from "../lib/types"; export class UserService { /** * Crée ou met à jour un utilisateur et retourne son UUID */ async upsertUserUuid(profile: UserProfile): Promise { const pool = getPool(); const client = await pool.connect(); try { // Créer un nouvel utilisateur avec UUID auto-généré const insertQuery = ` INSERT INTO users (first_name, last_name, team_id, uuid_id) VALUES ($1, $2, $3, uuid_generate_v4()) RETURNING uuid_id `; const result = await client.query(insertQuery, [ profile.firstName, profile.lastName, profile.teamId, ]); return result.rows[0].uuid_id; } catch (error: any) { // Si erreur de contrainte unique, l'utilisateur existe déjà if ( error.code === "23505" && error.constraint === "users_first_name_last_name_team_id_key" ) { // Récupérer l'utilisateur existant const existingUserQuery = ` SELECT uuid_id FROM users WHERE first_name = $1 AND last_name = $2 AND team_id = $3 `; const existingUser = await client.query(existingUserQuery, [ profile.firstName, profile.lastName, profile.teamId, ]); return existingUser.rows[0].uuid_id; } throw error; } finally { client.release(); } } /** * Met à jour un utilisateur existant par son UUID */ async updateUserByUuid( userUuid: string, profile: UserProfile ): Promise { const pool = getPool(); const client = await pool.connect(); try { // Vérifier s'il existe déjà un utilisateur avec ce nom/prénom dans la nouvelle équipe const conflictCheckQuery = ` SELECT uuid_id FROM users WHERE first_name = $1 AND last_name = $2 AND team_id = $3 AND uuid_id != $4 `; const conflictResult = await client.query(conflictCheckQuery, [ profile.firstName, profile.lastName, profile.teamId, userUuid, ]); if (conflictResult.rows.length > 0) { throw new Error( "Un utilisateur avec ce nom et prénom existe déjà dans cette équipe" ); } // Mettre à jour l'utilisateur existant const updateQuery = ` UPDATE users SET first_name = $1, last_name = $2, team_id = $3, updated_at = CURRENT_TIMESTAMP WHERE uuid_id = $4 `; await client.query(updateQuery, [ profile.firstName, profile.lastName, profile.teamId, userUuid, ]); } finally { client.release(); } } /** * Récupère un utilisateur par son UUID */ /** * Trouve un utilisateur par son profil (firstName, lastName) */ async findUserByProfile(profile: UserProfile): Promise<{ uuid: string; teamId: string; } | null> { const pool = getPool(); const client = await pool.connect(); try { const query = ` SELECT uuid_id, team_id FROM users WHERE first_name = $1 AND last_name = $2 `; const result = await client.query(query, [ profile.firstName, profile.lastName, ]); if (result.rows.length === 0) { return null; } return { uuid: result.rows[0].uuid_id, teamId: result.rows[0].team_id, }; } finally { client.release(); } } async getUserByUuid(userUuid: string): Promise { const pool = getPool(); const client = await pool.connect(); try { const query = ` SELECT u.first_name, u.last_name, u.team_id FROM users u WHERE u.uuid_id = $1 `; const result = await client.query(query, [userUuid]); if (result.rows.length === 0) { return null; } const user = result.rows[0]; return { firstName: user.first_name, lastName: user.last_name, teamId: user.team_id, }; } finally { client.release(); } } /** * Crée ou met à jour un utilisateur (legacy - retourne l'ID numérique) */ async upsertUser(profile: UserProfile): Promise { const pool = getPool(); const client = await pool.connect(); try { // Vérifier si l'utilisateur existe déjà (par firstName + lastName + teamId) const existingUserQuery = ` SELECT id FROM users WHERE first_name = $1 AND last_name = $2 AND team_id = $3 `; const existingUser = await client.query(existingUserQuery, [ profile.firstName, profile.lastName, profile.teamId, ]); if (existingUser.rows.length > 0) { // Mettre à jour l'utilisateur existant const updateQuery = ` UPDATE users SET first_name = $1, last_name = $2, team_id = $3, updated_at = CURRENT_TIMESTAMP WHERE id = $4 RETURNING id `; const result = await client.query(updateQuery, [ profile.firstName, profile.lastName, profile.teamId, existingUser.rows[0].id, ]); return result.rows[0].id; } else { // Créer un nouvel utilisateur const insertQuery = ` INSERT INTO users (first_name, last_name, team_id) VALUES ($1, $2, $3) RETURNING id `; const result = await client.query(insertQuery, [ profile.firstName, profile.lastName, profile.teamId, ]); return result.rows[0].id; } } finally { client.release(); } } /** * Delete user for admin */ static async deleteUserForAdmin( userId: string ): Promise<{ firstName: string; lastName: string }> { const pool = getPool(); const client = await pool.connect(); try { await client.query("BEGIN"); // Vérifier que l'utilisateur existe const userCheck = await client.query( "SELECT uuid_id, first_name, last_name, team_id FROM users WHERE uuid_id = $1", [userId] ); if (userCheck.rows.length === 0) { throw new Error("Utilisateur non trouvé"); } const user = userCheck.rows[0]; // Vérifier que l'utilisateur n'est pas dans une équipe if (user.team_id) { throw new Error( "Impossible de supprimer un utilisateur qui appartient à une équipe. Retirez-le d'abord de son équipe." ); } // Supprimer l'utilisateur (les évaluations par skills seront supprimées automatiquement grâce aux contraintes CASCADE) await client.query("DELETE FROM users WHERE uuid_id = $1", [userId]); await client.query("COMMIT"); return { firstName: user.first_name, lastName: user.last_name, }; } catch (error) { await client.query("ROLLBACK"); throw error; } finally { client.release(); } } /** * Get all users with team info and evaluations status for admin */ static async getAllUsersForAdmin(): Promise< Array<{ uuid: string; firstName: string; lastName: string; teamName: string | null; hasEvaluations: boolean; }> > { const pool = getPool(); const query = ` SELECT u.uuid_id, u.first_name, u.last_name, t.name as team_name, CASE WHEN ue.id IS NOT NULL THEN true ELSE false END as has_evaluations FROM users u LEFT JOIN teams t ON u.team_id = t.id LEFT JOIN user_evaluations ue ON u.uuid_id = ue.user_uuid ORDER BY u.first_name, u.last_name `; try { const result = await pool.query(query); return result.rows.map((row) => ({ uuid: row.uuid_id, firstName: row.first_name, lastName: row.last_name, teamName: row.team_name, hasEvaluations: row.has_evaluations, })); } catch (error) { console.error("Error fetching users:", error); throw new Error("Failed to fetch users"); } } } // Instance singleton export const userService = new UserService();