using GmRelay.Bot.Features.Sessions.CreateSession.Wizard;
using Telegram.Bot.Types;
using Xunit;

namespace GmRelay.Bot.Tests.Features.Sessions.CreateSession.Wizard;

/// <summary>
/// Verifies the Telegram <c>Update</c> → <c>WizardInteraction</c> mapping
/// that <see cref="WizardInteractionMapper"/> exposes. The mapper is the
/// single bridge between Telegram's native update type and the
/// platform-neutral wizard core, so its contract needs to be locked
/// down: callback queries carry the data payload, text messages carry
/// their text, and photos carry the largest photo's <c>FileId</c>.
/// </summary>
public sealed class WizardInteractionMapperTests
{
    [Fact]
    public void CallbackUpdate_ProducesCallbackInteraction_WithPayloadAndOwner()
    {
        var update = new Update
        {
            CallbackQuery = new CallbackQuery
            {
                Id = "cb-42",
                Data = "wizard:choice:Type:single",
                From = new User { Id = 100, FirstName = "GM" },
                Message = new Message { Chat = new Chat { Id = 42 } },
            },
        };

        var ok = WizardInteractionMapper.TryMap(update, out var interaction);

        Assert.True(ok);
        Assert.Equal("100", interaction.OwnerId);
        Assert.Null(interaction.Text);
        Assert.Equal("wizard:choice:Type:single", interaction.CallbackPayload);
        Assert.Null(interaction.PhotoFileId);
        Assert.Null(interaction.PhotoUrl);
        Assert.Equal("cb-42", interaction.InteractionId);
    }

    [Fact]
    public void TextUpdate_ProducesTextInteraction_WithTextAndNoCallback()
    {
        var update = new Update
        {
            Message = new Message
            {
                Text = "My Game Title",
                Chat = new Chat { Id = 42 },
                From = new User { Id = 200, FirstName = "GM" },
            },
        };

        var ok = WizardInteractionMapper.TryMap(update, out var interaction);

        Assert.True(ok);
        Assert.Equal("200", interaction.OwnerId);
        Assert.Equal("My Game Title", interaction.Text);
        Assert.Null(interaction.CallbackPayload);
        Assert.Null(interaction.PhotoFileId);
        Assert.Equal("msg", interaction.InteractionId);
    }

    [Fact]
    public void PhotoUpdate_ProducesPhotoInteraction_WithLargestFileId()
    {
        var update = new Update
        {
            Message = new Message
            {
                Chat = new Chat { Id = 42 },
                From = new User { Id = 300, FirstName = "GM" },
                Photo = new[]
                {
                    new PhotoSize { FileId = "small-id", Width = 90, Height = 60 },
                    new PhotoSize { FileId = "medium-id", Width = 320, Height = 240 },
                    new PhotoSize { FileId = "large-id", Width = 800, Height = 600 },
                },
            },
        };

        var ok = WizardInteractionMapper.TryMap(update, out var interaction);

        Assert.True(ok);
        Assert.Equal("300", interaction.OwnerId);
        Assert.Null(interaction.Text);
        Assert.Null(interaction.CallbackPayload);
        Assert.Equal("large-id", interaction.PhotoFileId);
    }

    [Fact]
    public void CaptionedPhoto_ProducesPhotoInteraction_AndKeepsCaptionOutOfText()
    {
        // Telegram sometimes attaches a caption to a photo message. The
        // mapper treats it as a non-text interaction (cover-step uses
        // PhotoFileId, not caption). This test pins that distinction.
        var update = new Update
        {
            Message = new Message
            {
                Caption = "ignored",
                Chat = new Chat { Id = 42 },
                From = new User { Id = 400 },
                Photo = new[]
                {
                    new PhotoSize { FileId = "only-id", Width = 100, Height = 100 },
                },
            },
        };

        var ok = WizardInteractionMapper.TryMap(update, out var interaction);

        Assert.True(ok);
        Assert.Equal("only-id", interaction.PhotoFileId);
    }

    [Fact]
    public void EmptyUpdate_ReturnsFalse()
    {
        var ok = WizardInteractionMapper.TryMap(new Update(), out var interaction);

        Assert.False(ok);
        Assert.Null(interaction);
    }
}
