using Dapper;
using GmRelay.DiscordBot.Infrastructure.Discord;
using GmRelay.Shared.Rendering;
using Npgsql;

namespace GmRelay.DiscordBot.Features.Sessions;

public sealed record DiscordDeleteSessionResult(
    string ReplyText,
    SessionBatchViewModel? UpdatedView,
    string? EmptyMessage = null);

public sealed class DiscordDeleteSessionHandler(
    NpgsqlDataSource dataSource,
    DiscordPermissionChecker permissionChecker,
    DiscordListSessionsHandler listSessionsHandler,
    ILogger<DiscordDeleteSessionHandler> logger)
{
    public async Task<DiscordDeleteSessionResult> HandleAsync(
        string guildId,
        string channelId,
        ulong userId,
        ulong resolvedPermissions,
        ulong guildOwnerId,
        Guid sessionId,
        CancellationToken cancellationToken)
    {
        await using var connection = await dataSource.OpenConnectionAsync(cancellationToken);

        var dbManagerUserIds = await connection.QueryAsync<ulong>(
            @"SELECT CAST(p.external_user_id AS BIGINT)
              FROM group_managers gm
              JOIN players p ON p.id = gm.player_id
              JOIN game_groups g ON g.id = gm.group_id
              WHERE g.platform = 'Discord'
                AND p.platform = 'Discord'
                AND g.external_group_id = @GuildId",
            new { GuildId = guildId });

        if (!permissionChecker.CanManageSchedule(guildOwnerId, userId, dbManagerUserIds, resolvedPermissions))
        {
            return new DiscordDeleteSessionResult(
                "Только owner, администратор или manager могут удалять сессии.",
                UpdatedView: null);
        }

        await using var transaction = await connection.BeginTransactionAsync(cancellationToken);
        await connection.ExecuteAsync(
            "SELECT pg_advisory_xact_lock(20260530, 108)",
            transaction: transaction);
        _ = await connection.QuerySingleOrDefaultAsync<Guid?>(
            """
            SELECT s.id
            FROM sessions s
            JOIN game_groups g ON g.id = s.group_id
            WHERE s.id = @SessionId
              AND g.platform = 'Discord'
              AND g.external_group_id = @GuildId
            FOR UPDATE OF s
            """,
            new { SessionId = sessionId, GuildId = guildId },
            transaction);

        await connection.ExecuteAsync(
            """
            UPDATE portfolio_games pg
            SET is_public = false,
                updated_at = now()
            FROM portfolio_game_sessions pgs
            JOIN sessions s ON s.id = pgs.session_id
            JOIN game_groups g ON g.id = s.group_id
            WHERE pgs.portfolio_game_id = pg.id
              AND s.id = @SessionId
              AND g.platform = 'Discord'
              AND g.external_group_id = @GuildId
              AND pg.is_public = true
            """,
            new { SessionId = sessionId, GuildId = guildId },
            transaction);

        var deletedRows = await connection.ExecuteAsync(
            """
            DELETE FROM sessions s
            USING game_groups g
            WHERE s.group_id = g.id
              AND s.id = @SessionId
              AND g.platform = 'Discord'
              AND g.external_group_id = @GuildId
            """,
            new { SessionId = sessionId, GuildId = guildId },
            transaction);

        await transaction.CommitAsync(cancellationToken);

        if (deletedRows == 0)
        {
            return new DiscordDeleteSessionResult(
                "Сессия не найдена или уже удалена.",
                UpdatedView: null);
        }

        logger.LogInformation("Deleted Discord session {SessionId} in guild {GuildId}", sessionId, guildId);

        var updatedView = await listSessionsHandler.BuildScheduleAsync(
            guildId,
            channelId,
            userId,
            resolvedPermissions,
            guildOwnerId,
            cancellationToken);

        return updatedView is null
            ? new DiscordDeleteSessionResult(
                "Сессия удалена.",
                UpdatedView: null,
                EmptyMessage: "В этом сервере нет предстоящих игр.")
            : new DiscordDeleteSessionResult("Сессия удалена.", updatedView);
    }
}
