using Dapper;
using GmRelay.Shared.Domain;
using Npgsql;

namespace GmRelay.Shared.Infrastructure.Scheduling;

public interface ISessionTriggerStore
{
    Task<IReadOnlyList<Guid>> GetSessionsNeedingConfirmationAsync(DateTimeOffset now, CancellationToken ct);
    Task<IReadOnlyList<Guid>> GetSessionsNeedingOneHourReminderAsync(DateTimeOffset now, CancellationToken ct);
    Task<IReadOnlyList<Guid>> 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<IReadOnlyList<Guid>> GetSessionsNeedingConfirmationAsync(DateTimeOffset now, CancellationToken ct)
    {
        await using var connection = await dataSource.OpenConnectionAsync(ct);

        var results = await connection.QueryAsync<Guid>(
            """
            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<IReadOnlyList<Guid>> GetSessionsNeedingOneHourReminderAsync(DateTimeOffset now, CancellationToken ct)
    {
        await using var connection = await dataSource.OpenConnectionAsync(ct);

        var results = await connection.QueryAsync<Guid>(
            """
            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<IReadOnlyList<Guid>> GetSessionsNeedingJoinLinkAsync(DateTimeOffset now, CancellationToken ct)
    {
        await using var connection = await dataSource.OpenConnectionAsync(ct);

        var results = await connection.QueryAsync<Guid>(
            """
            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();
    }
}
