import type { Database } from 'bun:sqlite'; import { ensureUserExists } from '../../db'; import { toIsoSqlUTC } from '../../utils/datetime'; /** * Reconciliación idempotente de membresías de un grupo. */ export function reconcileGroupMembers( db: Database, groupId: string, snapshot: Array<{ userId: string; isAdmin: boolean }>, nowIso?: string ): { added: number; updated: number; deactivated: number } { if (!groupId || !Array.isArray(snapshot)) { throw new Error('Invalid arguments for reconcileGroupMembers'); } const now = nowIso || toIsoSqlUTC(new Date()); let added = 0, updated = 0, deactivated = 0; const incoming = new Map(); for (const m of snapshot) { if (!m?.userId) continue; incoming.set(m.userId, { isAdmin: !!m.isAdmin }); } db.transaction(() => { const existingRows = db.prepare(` SELECT user_id, is_admin, is_active FROM group_members WHERE group_id = ? `).all(groupId) as Array<{ user_id: string; is_admin: number; is_active: number }>; const existing = new Map(existingRows.map(r => [r.user_id, { isAdmin: !!r.is_admin, isActive: !!r.is_active }])); for (const [userId, { isAdmin }] of incoming.entries()) { ensureUserExists(userId, db); const row = existing.get(userId); if (!row) { db.prepare(` INSERT INTO group_members (group_id, user_id, is_admin, is_active, first_seen_at, last_seen_at) VALUES (?, ?, ?, 1, ?, ?) `).run(groupId, userId, isAdmin ? 1 : 0, now, now); added++; } else { const roleChanged = row.isAdmin !== isAdmin; if (!row.isActive || roleChanged) { db.prepare(` UPDATE group_members SET is_active = 1, is_admin = ?, last_seen_at = ?, last_role_change_at = CASE WHEN ? THEN ? ELSE last_role_change_at END WHERE group_id = ? AND user_id = ? `).run(isAdmin ? 1 : 0, now, roleChanged ? 1 : 0, roleChanged ? now : null, groupId, userId); updated++; } else { db.prepare(` UPDATE group_members SET last_seen_at = ? WHERE group_id = ? AND user_id = ? `).run(now, groupId, userId); } } } for (const [userId, state] of existing.entries()) { if (!incoming.has(userId) && state.isActive) { db.prepare(` UPDATE group_members SET is_active = 0, last_seen_at = ? WHERE group_id = ? AND user_id = ? `).run(now, groupId, userId); deactivated++; } } })(); return { added, updated, deactivated }; }