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
{
    // NOTE: NativeAOT — Dapper.AOT 1.0.48 only generates interceptors for the
    // (sql, param) extension overloads, NOT for the (CommandDefinition) overload.
    // Passing a CommandDefinition here would skip the interceptor and fall back to
    // Dapper.SqlMapper.CreateParamInfoGenerator, which uses Reflection.Emit and
    // throws PlatformNotSupportedException on AOT (issue: wizard silently dropped
    // every Telegram update in v3.9.0). All four methods therefore use the plain
    // (sql, param) overload, matching the pattern in JoinSessionHandler.
    public async Task<WizardDraft?> GetActiveAsync(string platform, string ownerId, CancellationToken ct)
    {
        const string sql = """
            SELECT id              AS Id,
                   chat_id         AS ChatId,
                   message_thread_id AS MessageThreadId,
                   owner_id        AS OwnerId,
                   platform        AS Platform,
                   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 platform = @Platform
              AND owner_id = @OwnerId
              AND expires_at > NOW()
            ORDER BY updated_at DESC
            LIMIT 1
            """;
        await using var connection = await dataSource.OpenConnectionAsync(ct);
        return await connection.QuerySingleOrDefaultAsync<WizardDraft>(
            sql,
            new { Platform = platform, OwnerId = ownerId });
    }

    public async Task UpsertAsync(WizardDraft draft, CancellationToken ct)
    {
        const string sql = """
            INSERT INTO wizard_drafts
                (id, chat_id, message_thread_id, owner_id, platform, step, payload, draft_message_id, created_at, updated_at, expires_at)
            VALUES
                (@Id, @ChatId, @MessageThreadId, @OwnerId, @Platform, @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(sql, draft);
    }

    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(sql, new { Id = id });
    }

    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(sql);
    }
}
