using Dapper;
using GmRelay.Shared.Domain;
using GmRelay.Shared.Platform;
using Microsoft.Extensions.Logging;
using Npgsql;

namespace GmRelay.Shared.Features.Sessions.ListSessions;

internal sealed record DeleteSessionInfoDto(
    string Title,
    Guid BatchId,
    Guid GroupId,
    bool CanManage,
    int? ThreadId,
    bool TopicCreatedByBot);

public sealed record DeleteSessionResult(
    bool Success,
    string? ReplyText,
    string? Title,
    Guid? GroupId,
    int? ThreadId,
    bool TopicCreatedByBot,
    int RemainingInTopic);

public sealed class DeleteSessionHandler(
    NpgsqlDataSource dataSource)
{
    public async Task<DeleteSessionResult> HandleAsync(DeleteSessionCommand command, CancellationToken ct)
    {
        await using var connection = await dataSource.OpenConnectionAsync(ct);
        await using var transaction = await connection.BeginTransactionAsync(ct);

        // 1. Use the database mutation order before locking the session or linked portfolio cards.
        await connection.ExecuteAsync(
            "SELECT pg_advisory_xact_lock(20260530, 108)",
            transaction: transaction);

        // 2. Lock the session before any linked portfolio card and verify group manager.
        var session = await connection.QuerySingleOrDefaultAsync<DeleteSessionInfoDto>(
            """
            SELECT s.title AS Title,
                   s.batch_id AS BatchId,
                   s.group_id AS GroupId,
                   s.thread_id AS ThreadId,
                   s.topic_created_by_bot AS TopicCreatedByBot,
                   EXISTS (
                       SELECT 1
                       FROM group_managers gm
                       JOIN players p ON p.id = gm.player_id
                       WHERE gm.group_id = s.group_id
                         AND p.platform = @Platform
                       AND p.external_user_id = @ExternalUserId
                   ) AS CanManage
            FROM sessions s
            WHERE s.id = @SessionId
            FOR UPDATE OF s
            """,
            new { command.SessionId, Platform = command.User.Platform.ToString(), ExternalUserId = command.User.ExternalUserId }, transaction);

        if (session == null)
        {
            return new DeleteSessionResult(false, "Сессия не найдена.", null, null, null, false, 0);
        }

        if (!session.CanManage)
        {
            return new DeleteSessionResult(false, "Только owner или co-GM может удалять сессию.", null, null, null, false, 0);
        }

        // 3. Unpublish a linked portfolio card before its required session link cascades away.
        await connection.ExecuteAsync(
            """
            UPDATE portfolio_games pg
            SET is_public = false,
                updated_at = now()
            FROM portfolio_game_sessions pgs
            WHERE pgs.portfolio_game_id = pg.id
              AND pgs.session_id = @SessionId
              AND pg.is_public = true
            """,
            new { command.SessionId },
            transaction);

        // 4. Delete session
        await connection.ExecuteAsync("DELETE FROM sessions WHERE id = @Id", new { Id = command.SessionId }, transaction);

        var remainingInTopic = session.ThreadId.HasValue
            ? await connection.ExecuteScalarAsync<int>(
                """
                SELECT COUNT(*)
                FROM sessions
                WHERE group_id = @GroupId
                  AND thread_id = @ThreadId
                """,
                new { session.GroupId, ThreadId = session.ThreadId.Value },
                transaction)
            : 0;

        await transaction.CommitAsync(ct);

        return new DeleteSessionResult(
            true,
            "Сессия удалена!",
            session.Title,
            session.GroupId,
            session.ThreadId,
            session.TopicCreatedByBot,
            remainingInTopic);
    }
}
