using GmRelay.DiscordBot.Rendering;
using GmRelay.Shared.Domain;
using GmRelay.Shared.Rendering;
using NetCord;
using NetCord.Rest;

namespace GmRelay.Bot.Tests.Rendering;

public sealed class DiscordSessionBatchRendererTests
{
    [Fact]
    public void Render_ShouldProduceEmbedsAndButtonsForMultipleSessions()
    {
        var firstSessionId = Guid.NewGuid();
        var secondSessionId = Guid.NewGuid();
        var cancelledSessionId = Guid.NewGuid();

        var sessions = new[]
        {
            new SessionBatchDto(secondSessionId, new DateTime(2026, 4, 27, 18, 0, 0, DateTimeKind.Utc), SessionStatus.Planned, 4, "https://example.com/game2"),
            new SessionBatchDto(cancelledSessionId, new DateTime(2026, 4, 28, 18, 0, 0, DateTimeKind.Utc), SessionStatus.Cancelled, null, ""),
            new SessionBatchDto(firstSessionId, new DateTime(2026, 4, 26, 18, 0, 0, DateTimeKind.Utc), SessionStatus.Planned, 2, "https://example.com/game1")
        };
        var participants = new[]
        {
            new ParticipantBatchDto(secondSessionId, "Alice", "alice", ParticipantRegistrationStatus.Active),
            new ParticipantBatchDto(secondSessionId, "Charlie", null, ParticipantRegistrationStatus.Waitlisted),
            new ParticipantBatchDto(cancelledSessionId, "Bob", null, ParticipantRegistrationStatus.Active)
        };

        var view = SessionBatchViewBuilder.Build("Campaign", sessions, participants);
        var (embeds, actionRows) = DiscordSessionBatchRenderer.Render(view);

        Assert.Equal(3, embeds.Count);
        Assert.Equal(2, actionRows.Count); // cancelled skipped

        // Embed titles contain game title and Moscow date
        Assert.Contains(embeds, e => e.Title!.Contains("Campaign") && e.Title.Contains("26"));
        Assert.Contains(embeds, e => e.Title!.Contains("Campaign") && e.Title.Contains("27"));
        Assert.Contains(embeds, e => e.Title!.Contains("Campaign") && e.Title.Contains("28"));

        // Cancelled session embed description indicates cancellation
        var cancelledEmbed = embeds.First(e => e.Description!.Contains("отменена") || e.Description.Contains("Отменена"));
        Assert.NotNull(cancelledEmbed);

        // Active session embeds contain player names
        Assert.Contains(embeds, e => e.Description!.Contains("Alice"));
        Assert.Contains(embeds, e => e.Description!.Contains("Charlie"));

        // Buttons for active sessions
        var allButtons = actionRows.SelectMany(r => r).OfType<ButtonProperties>().ToList();
        Assert.Contains(allButtons, b => b.CustomId == $"join_session:{firstSessionId}");
        Assert.Contains(allButtons, b => b.CustomId == $"leave_session:{firstSessionId}");
        Assert.Contains(allButtons, b => b.CustomId == $"join_session:{secondSessionId}");
        Assert.Contains(allButtons, b => b.CustomId == $"leave_session:{secondSessionId}");
    }

    [Fact]
    public void Render_ShouldSkipActionRowsForCancelledSessions()
    {
        var cancelledSessionId = Guid.NewGuid();
        var sessions = new[] { new SessionBatchDto(cancelledSessionId, DateTime.UtcNow, SessionStatus.Cancelled, null, "") };
        var participants = Array.Empty<ParticipantBatchDto>();

        var view = SessionBatchViewBuilder.Build("Test", sessions, participants);
        var (embeds, actionRows) = DiscordSessionBatchRenderer.Render(view);

        Assert.Single(embeds);
        Assert.Empty(actionRows);
    }

    [Fact]
    public void Render_ShouldShowWaitlistButtonWhenFull()
    {
        var sessionId = Guid.NewGuid();
        var sessions = new[] { new SessionBatchDto(sessionId, DateTime.UtcNow, SessionStatus.Planned, 1, "https://example.com/game") };
        var participants = new[] { new ParticipantBatchDto(sessionId, "Alice", "alice", ParticipantRegistrationStatus.Active) };

        var view = SessionBatchViewBuilder.Build("Test", sessions, participants);
        var (_, actionRows) = DiscordSessionBatchRenderer.Render(view);
        var buttons = actionRows.SelectMany(r => r).OfType<ButtonProperties>().ToList();
        var joinButton = buttons.First(b => b.CustomId == $"join_session:{sessionId}");

        Assert.Contains("ожидания", joinButton.Label);
    }

    [Fact]
    public void Render_ShouldUseRedColorForCancelledSessions()
    {
        var sessionId = Guid.NewGuid();
        var sessions = new[] { new SessionBatchDto(sessionId, DateTime.UtcNow, SessionStatus.Cancelled, null, "") };
        var participants = Array.Empty<ParticipantBatchDto>();

        var view = SessionBatchViewBuilder.Build("Test", sessions, participants);
        var (embeds, _) = DiscordSessionBatchRenderer.Render(view);

        Assert.Equal(0xED4245, embeds[0].Color.RawValue);
    }

    [Fact]
    public void Render_ShouldUseGreenColorForOpenSessions()
    {
        var sessionId = Guid.NewGuid();
        var sessions = new[] { new SessionBatchDto(sessionId, DateTime.UtcNow, SessionStatus.Planned, 4, "https://example.com/game") };
        var participants = Array.Empty<ParticipantBatchDto>();

        var view = SessionBatchViewBuilder.Build("Test", sessions, participants);
        var (embeds, _) = DiscordSessionBatchRenderer.Render(view);

        Assert.Equal(0x57F287, embeds[0].Color.RawValue);
    }

    [Fact]
    public void Render_ShouldUseYellowColorForFullSessions()
    {
        var sessionId = Guid.NewGuid();
        var sessions = new[] { new SessionBatchDto(sessionId, DateTime.UtcNow, SessionStatus.Planned, 1, "https://example.com/game") };
        var participants = new[] { new ParticipantBatchDto(sessionId, "Alice", "alice", ParticipantRegistrationStatus.Active) };

        var view = SessionBatchViewBuilder.Build("Test", sessions, participants);
        var (embeds, _) = DiscordSessionBatchRenderer.Render(view);

        Assert.Equal(0xFEE75C, embeds[0].Color.RawValue);
    }

    [Fact]
    public void Render_ShouldHandleRescheduleStatus()
    {
        var sessionId = Guid.NewGuid();
        var sessions = new[] { new SessionBatchDto(sessionId, DateTime.UtcNow, "Rescheduled", 4, "") };
        var participants = Array.Empty<ParticipantBatchDto>();

        var view = SessionBatchViewBuilder.Build("Test", sessions, participants);
        var (embeds, actionRows) = DiscordSessionBatchRenderer.Render(view);

        Assert.Single(embeds);
        Assert.Single(actionRows); // not cancelled → actions present
    }

    [Fact]
    public void Render_ShouldUseBlueColorForConfirmedSessions()
    {
        var sessionId = Guid.NewGuid();
        var sessions = new[] { new SessionBatchDto(sessionId, DateTime.UtcNow, SessionStatus.Confirmed, 4, "https://example.com/game") };
        var participants = Array.Empty<ParticipantBatchDto>();

        var view = SessionBatchViewBuilder.Build("Test", sessions, participants);
        var (embeds, _) = DiscordSessionBatchRenderer.Render(view);

        Assert.Equal(0x5865F2, embeds[0].Color.RawValue);
    }

    [Fact]
    public void Render_ShouldShowEmptyPlayerDescription()
    {
        var sessionId = Guid.NewGuid();
        var sessions = new[] { new SessionBatchDto(sessionId, DateTime.UtcNow, SessionStatus.Planned, 4, "https://example.com/game") };
        var participants = Array.Empty<ParticipantBatchDto>();

        var view = SessionBatchViewBuilder.Build("Test", sessions, participants);
        var (embeds, _) = DiscordSessionBatchRenderer.Render(view);

        Assert.Contains("Пока никто не записался", embeds[0].Description);
    }

    [Fact]
    public void Render_ShouldSetEmbedUrlWhenJoinLinkPresent()
    {
        var sessionId = Guid.NewGuid();
        var sessions = new[] { new SessionBatchDto(sessionId, DateTime.UtcNow, SessionStatus.Planned, 4, "https://example.com/game") };
        var participants = Array.Empty<ParticipantBatchDto>();

        var view = SessionBatchViewBuilder.Build("Test", sessions, participants);
        var (embeds, _) = DiscordSessionBatchRenderer.Render(view);

        Assert.Equal("https://example.com/game", embeds[0].Url);
    }

    [Fact]
    public void Render_ShouldNormalizeBareDomainJoinLinkForEmbedUrl()
    {
        var sessionId = Guid.NewGuid();
        var sessions = new[] { new SessionBatchDto(sessionId, DateTime.UtcNow, SessionStatus.Planned, 4, "mobaxterm.mobatek.net/game") };
        var participants = Array.Empty<ParticipantBatchDto>();

        var view = SessionBatchViewBuilder.Build("Test", sessions, participants);
        var (embeds, _) = DiscordSessionBatchRenderer.Render(view);

        Assert.Equal("https://mobaxterm.mobatek.net/game", embeds[0].Url);
    }

    [Fact]
    public void Render_ShouldNotSetEmbedUrlWhenJoinLinkIsNotHttpUrl()
    {
        var sessionId = Guid.NewGuid();
        var sessions = new[] { new SessionBatchDto(sessionId, DateTime.UtcNow, SessionStatus.Planned, 4, "test") };
        var participants = Array.Empty<ParticipantBatchDto>();

        var view = SessionBatchViewBuilder.Build("Test", sessions, participants);
        var (embeds, _) = DiscordSessionBatchRenderer.Render(view);

        Assert.Null(embeds[0].Url);
    }

    [Fact]
    public void Render_ShouldEmbedCorrectFieldValues()
    {
        var sessionId = Guid.NewGuid();
        var sessions = new[] { new SessionBatchDto(sessionId, DateTime.UtcNow, SessionStatus.Planned, 4, "https://example.com/game") };
        var participants = new[]
        {
            new ParticipantBatchDto(sessionId, "Alice", "alice", ParticipantRegistrationStatus.Active),
            new ParticipantBatchDto(sessionId, "Bob", null, ParticipantRegistrationStatus.Waitlisted)
        };

        var view = SessionBatchViewBuilder.Build("Test", sessions, participants);
        var (embeds, _) = DiscordSessionBatchRenderer.Render(view);
        var embed = embeds[0];
        var fields = embed.Fields!.ToList();

        Assert.Equal(3, fields.Count);
        Assert.Equal("👥 Заполненность", fields[0].Name);
        Assert.Equal("1/4", fields[0].Value);
        Assert.Equal("⏳ Лист ожидания", fields[1].Name);
        Assert.Equal("1", fields[1].Value);
        Assert.Equal("📊 Статус", fields[2].Name);
        Assert.Equal("Запланирована", fields[2].Value);
    }
}
