using System;
using System.Threading;
using System.Threading.Tasks;
using Dapper;
using Npgsql;

namespace GmRelay.Shared.Features.Sessions.CreateSession.Wizard;

public sealed class WizardDraftRepository(NpgsqlDataSource dataSource) : IWizardDraftRepository
{
    public async Task<WizardDraft?> GetActiveAsync(
        long chatId, int? messageThreadId, long ownerTelegramId, CancellationToken ct)
    {
        const string sql = """
            SELECT id              AS Id,
                   chat_id         AS ChatId,
                   message_thread_id AS MessageThreadId,
                   owner_telegram_id AS OwnerTelegramId,
                   step            AS Step,
                   payload::text   AS PayloadJson,
                   draft_message_id AS DraftMessageId,
                   created_at      AS CreatedAt,
                   updated_at      AS UpdatedAt,
                   expires_at      AS ExpiresAt
            FROM wizard_drafts
            WHERE chat_id = @ChatId
              AND (message_thread_id = @ThreadId OR (@ThreadId IS NULL AND message_thread_id IS NULL))
              AND owner_telegram_id = @OwnerId
              AND expires_at > NOW()
            LIMIT 1
            """;

        await using var connection = await dataSource.OpenConnectionAsync(ct);
        return await connection.QuerySingleOrDefaultAsync<WizardDraft>(
            new CommandDefinition(sql,
                new { ChatId = chatId, ThreadId = messageThreadId, OwnerId = ownerTelegramId },
                cancellationToken: ct));
    }

    public async Task UpsertAsync(WizardDraft draft, CancellationToken ct)
    {
        const string sql = """
            INSERT INTO wizard_drafts
                (id, chat_id, message_thread_id, owner_telegram_id, step, payload, draft_message_id, created_at, updated_at, expires_at)
            VALUES
                (@Id, @ChatId, @MessageThreadId, @OwnerTelegramId, @Step, @PayloadJson::jsonb, @DraftMessageId, @CreatedAt, @UpdatedAt, @ExpiresAt)
            ON CONFLICT (id) DO UPDATE
                SET step = EXCLUDED.step,
                    payload = EXCLUDED.payload,
                    draft_message_id = EXCLUDED.draft_message_id,
                    updated_at = EXCLUDED.updated_at,
                    expires_at = EXCLUDED.expires_at;
            """;

        await using var connection = await dataSource.OpenConnectionAsync(ct);
        await connection.ExecuteAsync(new CommandDefinition(sql, draft, cancellationToken: ct));
    }

    public async Task DeleteAsync(Guid id, CancellationToken ct)
    {
        const string sql = "DELETE FROM wizard_drafts WHERE id = @Id";
        await using var connection = await dataSource.OpenConnectionAsync(ct);
        await connection.ExecuteAsync(new CommandDefinition(sql, new { Id = id }, cancellationToken: ct));
    }

    public async Task<int> DeleteExpiredAsync(CancellationToken ct)
    {
        const string sql = "DELETE FROM wizard_drafts WHERE expires_at <= NOW()";
        await using var connection = await dataSource.OpenConnectionAsync(ct);
        return await connection.ExecuteAsync(new CommandDefinition(sql, cancellationToken: ct));
    }
}
