using Dapper; using GmRelay.Shared.Domain; using Npgsql; namespace GmRelay.Shared.Infrastructure.Scheduling; public interface ISessionTriggerStore { Task> GetSessionsNeedingConfirmationAsync(DateTimeOffset now, CancellationToken ct); Task> GetSessionsNeedingOneHourReminderAsync(DateTimeOffset now, CancellationToken ct); Task> GetSessionsNeedingJoinLinkAsync(DateTimeOffset now, CancellationToken ct); } public sealed class DbSessionTriggerStore( NpgsqlDataSource dataSource, PlatformSchedulerOptions options) : ISessionTriggerStore { private static readonly TimeSpan ConfirmationLeadTime = TimeSpan.FromHours(24); private static readonly TimeSpan OneHourReminderLeadTime = TimeSpan.FromHours(1); private static readonly TimeSpan JoinLinkLeadTime = TimeSpan.FromMinutes(5); public async Task> GetSessionsNeedingConfirmationAsync(DateTimeOffset now, CancellationToken ct) { await using var connection = await dataSource.OpenConnectionAsync(ct); var results = await connection.QueryAsync( """ SELECT s.id FROM sessions s JOIN game_groups g ON g.id = s.group_id WHERE g.platform = @Platform AND s.status = @Planned AND s.scheduled_at - @LeadTime <= @Now AND s.confirmation_sent_at IS NULL """, new { Platform = options.Platform.ToString(), Planned = SessionStatus.Planned, LeadTime = ConfirmationLeadTime, Now = now.UtcDateTime }); return results.ToList(); } public async Task> GetSessionsNeedingOneHourReminderAsync(DateTimeOffset now, CancellationToken ct) { await using var connection = await dataSource.OpenConnectionAsync(ct); var results = await connection.QueryAsync( """ SELECT s.id FROM sessions s JOIN game_groups g ON g.id = s.group_id WHERE g.platform = @Platform AND s.status IN (@Confirmed, @ConfirmationSent) AND s.scheduled_at - @LeadTime <= @Now AND s.one_hour_reminder_processed_at IS NULL """, new { Platform = options.Platform.ToString(), Confirmed = SessionStatus.Confirmed, ConfirmationSent = SessionStatus.ConfirmationSent, LeadTime = OneHourReminderLeadTime, Now = now.UtcDateTime }); return results.ToList(); } public async Task> GetSessionsNeedingJoinLinkAsync(DateTimeOffset now, CancellationToken ct) { await using var connection = await dataSource.OpenConnectionAsync(ct); var results = await connection.QueryAsync( """ SELECT s.id FROM sessions s JOIN game_groups g ON g.id = s.group_id WHERE g.platform = @Platform AND s.status = @Confirmed AND s.scheduled_at - @LeadTime <= @Now AND ( (g.platform = 'Telegram' AND s.link_message_id IS NULL) OR ( g.platform <> 'Telegram' AND NOT EXISTS ( SELECT 1 FROM platform_messages pm WHERE pm.session_id = s.id AND pm.platform = g.platform AND pm.purpose = 'join_link' ) ) ) """, new { Platform = options.Platform.ToString(), Confirmed = SessionStatus.Confirmed, LeadTime = JoinLinkLeadTime, Now = now.UtcDateTime }); return results.ToList(); } }