// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using Microsoft.Teams.Apps.Schema;
using Microsoft.Teams.Apps.Schema.Entities;
using Microsoft.Teams.Core.Schema;

namespace Microsoft.Teams.Apps.UnitTests;

public class TeamsActivityBuilderTests
{
    private readonly TeamsActivityBuilder builder;
    private readonly TeamsActivityBuilder messageBuilder;
    public TeamsActivityBuilderTests()
    {
        builder = TeamsActivity.CreateBuilder();
        messageBuilder = TeamsActivity.CreateBuilder(new MessageActivity());
    }

    [Fact]
    public void Constructor_DefaultConstructor_CreatesNewActivity()
    {
        TeamsActivity activity = TeamsActivity.CreateBuilder().Build();

        Assert.NotNull(activity);
        Assert.Null(activity.From);
        Assert.Null(activity.Recipient);
        Assert.Null(activity.Conversation);
    }

    [Fact]
    public void Constructor_WithExistingActivity_UsesProvidedActivity()
    {
        TeamsActivity existingActivity = new()
        {
            Id = "test-id"
        };
        existingActivity.Properties["text"] = "existing text";

        TeamsActivityBuilder taBuilder = TeamsActivity.CreateBuilder(existingActivity);
        TeamsActivity activity = taBuilder.Build();

        Assert.Equal("test-id", activity.Id);
        Assert.Equal("existing text", activity.Properties["text"]);
    }

    [Fact]
    public void Constructor_WithNullActivity_ThrowsArgumentNullException()
    {
        Assert.Throws<ArgumentNullException>(() => TeamsActivity.CreateBuilder(null!));
    }

    [Fact]
    public void WithId_SetsActivityId()
    {
        TeamsActivity activity = builder
            .WithId("test-activity-id")
            .Build();

        Assert.Equal("test-activity-id", activity.Id);
    }

    [Fact]
    public void WithServiceUrl_SetsServiceUrl()
    {
        Uri serviceUrl = new("https://smba.trafficmanager.net/teams/");

        TeamsActivity activity = builder
            .WithServiceUrl(serviceUrl)
            .Build();

        Assert.Equal(serviceUrl, activity.ServiceUrl);
    }

    [Fact]
    public void WithChannelId_SetsChannelId()
    {
        TeamsActivity activity = builder
            .WithChannelId("msteams")
            .Build();

        Assert.Equal("msteams", activity.ChannelId);
    }

    [Fact]
    public void WithType_SetsActivityType()
    {
        TeamsActivity activity = builder
            .WithType(TeamsActivityType.Message)
            .Build();

        Assert.Equal(TeamsActivityType.Message, activity.Type);
    }

    [Fact]
    public void WithText_SetsTextContent()
    {
        TeamsActivity activity = builder
            .WithText("Hello, World!")
            .Build();

        Assert.Equal("Hello, World!", activity.Properties["text"]);
    }

    [Fact]
    public void WithFrom_SyncsBaseProperty()
    {
        TeamsActivity incoming = TeamsActivity.FromActivity(CoreActivity.FromJsonString(json));
        TeamsActivity reply = TeamsActivity.CreateBuilder()
            .WithConversationReference(incoming)
            .WithText("test")
            .Build();

        CoreActivity coreRef = reply;
        Assert.NotNull(coreRef.From);
        Assert.Equal(incoming.Recipient?.Id, coreRef.From.Id);

        ConversationAccount fromWithAgentic = new() { Id = "bot1", AgenticAppId = "app-1" };
        TeamsActivity agenticReply = TeamsActivity.CreateBuilder()
            .WithConversationReference(incoming)
            .WithFrom(fromWithAgentic)
            .Build();

        CoreActivity agenticCoreRef = agenticReply;
        Assert.NotNull(agenticCoreRef.From);
        Assert.Equal("app-1", agenticCoreRef.From.AgenticAppId);
        Assert.NotNull(AgenticIdentity.FromAccount(agenticCoreRef.From));
    }

    [Fact]
    public void WithFrom_DoesNotProduceDuplicateFromInJson()
    {
        TeamsActivity incoming = TeamsActivity.FromActivity(CoreActivity.FromJsonString(json));
        TeamsActivity reply = TeamsActivity.CreateBuilder()
            .WithConversationReference(incoming)
            .WithText("hello")
            .Build();

        string serialized = reply.ToJson();

        int fromCount = System.Text.RegularExpressions.Regex.Matches(serialized, "\"from\"\\s*:").Count;
        Assert.Equal(1, fromCount);
    }

    [Fact]
    public void WithRecipient_DoesNotProduceDuplicateRecipientInJson()
    {
        TeamsActivity incoming = TeamsActivity.FromActivity(CoreActivity.FromJsonString(json));
        TeamsActivity reply = TeamsActivity.CreateBuilder()
            .WithConversationReference(incoming)
            .WithRecipient(incoming.From)
            .WithText("hello")
            .Build();

        string serialized = reply.ToJson();

        int recipientCount = System.Text.RegularExpressions.Regex.Matches(serialized, "\"recipient\"\\s*:").Count;
        Assert.Equal(1, recipientCount);
    }

    [Fact]
    public void WithFrom_SetsSenderAccount()
    {
        TeamsConversationAccount? fromAccount = TeamsConversationAccount.FromConversationAccount(new ConversationAccount
        {
            Id = "sender-id",
            Name = "Sender Name"
        });

        TeamsActivity activity = builder
            .WithFrom(fromAccount)
            .Build();

        Assert.Equal("sender-id", activity.From?.Id);
        Assert.Equal("Sender Name", activity.From?.Name);
    }

    [Fact]
    public void WithRecipient_SetsRecipientAccount()
    {
        TeamsConversationAccount? recipientAccount = TeamsConversationAccount.FromConversationAccount(new ConversationAccount
        {
            Id = "recipient-id",
            Name = "Recipient Name"
        });
        Assert.NotNull(recipientAccount);
        TeamsActivity activity = builder
            .WithRecipient(recipientAccount)
            .Build();

        Assert.Equal("recipient-id", activity.Recipient?.Id);
        Assert.Equal("Recipient Name", activity.Recipient?.Name);
    }

    [Fact]
    public void FromConversationAccount_PreservesTenantId()
    {
        TeamsConversationAccount source = new()
        {
            Id = "user-id",
            Name = "User Name",
            TenantId = "tenant-abc",
            AgenticAppId = "app-1",
            AgenticUserId = "user-1",
            AgenticAppBlueprintId = "bp-1",
        };

        TeamsConversationAccount? result = TeamsConversationAccount.FromConversationAccount(source);

        Assert.NotNull(result);
        Assert.Equal("tenant-abc", result.TenantId);
        Assert.Equal("app-1", result.AgenticAppId);
        Assert.Equal("user-1", result.AgenticUserId);
        Assert.Equal("bp-1", result.AgenticAppBlueprintId);
    }

    [Fact]
    public void WithConversation_SetsConversationInfo()
    {
        Conversation baseConversation = new("conversation-id");

        Assert.NotNull(baseConversation);
        baseConversation.Properties.Add("tenantId", "tenant-123");
        baseConversation.Properties.Add("conversationType", "channel");
        TeamsConversation? conversation = TeamsConversation.FromConversation(baseConversation);

        TeamsActivity activity = builder
            .WithConversation(conversation)
            .Build();

        Assert.Equal("conversation-id", activity.Conversation?.Id);
        Assert.Equal("tenant-123", activity.Conversation?.TenantId);
        Assert.Equal("channel", activity.Conversation?.ConversationType);
    }

    [Fact]
    public void WithChannelData_SetsChannelData()
    {
        TeamsChannelData channelData = new()
        {
            TeamsChannelId = "19:channel-id@thread.tacv2",
            TeamsTeamId = "19:team-id@thread.tacv2"
        };

        TeamsActivity activity = builder
            .WithChannelData(channelData)
            .Build();

        Assert.NotNull(activity.ChannelData);
        Assert.Equal("19:channel-id@thread.tacv2", activity.ChannelData?.TeamsChannelId);
        Assert.Equal("19:team-id@thread.tacv2", activity.ChannelData?.TeamsTeamId);
    }

    [Fact]
    public void WithEntities_SetsEntitiesCollection()
    {
        EntityList entities =
        [
            new ClientInfoEntity
            {
                Locale = "en-US",
                Platform = "Web"
            }
        ];

        TeamsActivity activity = builder
            .WithEntities(entities)
            .Build();

        Assert.NotNull(activity.Entities);
        Assert.Single(activity.Entities);
        Assert.IsType<ClientInfoEntity>(activity.Entities[0]);
    }

    [Fact]
    public void WithAttachments_SetsAttachmentsCollection()
    {
        List<TeamsAttachment> attachments =
        [
            new() {
                ContentType = "application/json",
                Name = "test-attachment"
            }
        ];

        MessageActivity activity = (MessageActivity)messageBuilder
            .WithAttachments(attachments)
            .Build();

        Assert.NotNull(activity.Attachments);
        Assert.Single(activity.Attachments);
        Assert.Equal("application/json", activity.Attachments[0].ContentType);
        Assert.Equal("test-attachment", activity.Attachments[0].Name);
    }

    [Fact]
    public void WithAttachment_SetsSingleAttachment()
    {
        TeamsAttachment attachment = new()
        {
            ContentType = "application/json",
            Name = "single"
        };

        MessageActivity activity = (MessageActivity)messageBuilder
            .WithAttachment(attachment)
            .Build();

        Assert.NotNull(activity.Attachments);
        Assert.Single(activity.Attachments);
        Assert.Equal("single", activity.Attachments[0].Name);
    }

    [Fact]
    public void AddEntity_AddsEntityToCollection()
    {
        ClientInfoEntity entity = new()
        {
            Locale = "en-US",
            Country = "US"
        };

        TeamsActivity activity = builder
            .AddEntity(entity)
            .Build();

        Assert.NotNull(activity.Entities);
        Assert.Single(activity.Entities);
        Assert.IsType<ClientInfoEntity>(activity.Entities[0]);
    }

    [Fact]
    public void AddEntity_MultipleEntities_AddsAllToCollection()
    {
        TeamsActivity activity = builder
            .AddEntity(new ClientInfoEntity { Locale = "en-US" })
            .AddEntity(new ProductInfoEntity { Id = "product-123" })
            .Build();

        Assert.NotNull(activity.Entities);
        Assert.Equal(2, activity.Entities?.Count);
    }

    [Fact]
    public void AddClientInfo_AddsClientInfoEntity()
    {
        TeamsActivity activity = builder
            .AddClientInfo("Web", "US", "America/Los_Angeles", "en-US")
            .Build();

        ClientInfoEntity? entity = activity.Entities?.OfType<ClientInfoEntity>().SingleOrDefault();
        Assert.NotNull(entity);
        Assert.Equal("Web", entity.Platform);
        Assert.Equal("US", entity.Country);
        Assert.Equal("America/Los_Angeles", entity.Timezone);
        Assert.Equal("en-US", entity.Locale);
    }

    [Fact]
    public void AddProductInfo_AddsProductInfoEntity()
    {
        TeamsActivity activity = builder
            .AddProductInfo("product-123")
            .Build();

        ProductInfoEntity? entity = activity.Entities?.OfType<ProductInfoEntity>().SingleOrDefault();
        Assert.NotNull(entity);
        Assert.Equal("product-123", entity.Id);
    }

    [Fact]
    public void AddFeedback_WithMode_SetsFeedbackLoopAndClearsFeedbackLoopEnabled()
    {
        TeamsActivity activity = builder
            .AddFeedback(FeedbackType.Custom)
            .Build();

        Assert.NotNull(activity.ChannelData);
        Assert.Null(activity.ChannelData.FeedbackLoopEnabled);
        Assert.NotNull(activity.ChannelData.FeedbackLoop);
        Assert.Equal(FeedbackType.Custom, activity.ChannelData.FeedbackLoop.Type);
    }

    [Fact]
    public void AddAttachment_AddsAttachmentToCollection()
    {
        TeamsAttachment attachment = new()
        {
            ContentType = "text/html",
            Name = "test.html"
        };

        MessageActivity activity = (MessageActivity)messageBuilder
            .AddAttachment(attachment)
            .Build();

        Assert.NotNull(activity.Attachments);
        Assert.Single(activity.Attachments);
        Assert.Equal("text/html", activity.Attachments[0].ContentType);
    }

    [Fact]
    public void AddAttachment_MultipleAttachments_AddsAllToCollection()
    {
        MessageActivity activity = (MessageActivity)messageBuilder
            .AddAttachment(new TeamsAttachment { ContentType = "text/html" })
            .AddAttachment(new TeamsAttachment { ContentType = "application/json" })
            .Build();

        Assert.NotNull(activity.Attachments);
        Assert.Equal(2, activity.Attachments?.Count);
    }

    [Fact]
    public void AddAdaptiveCardAttachment_AddsAdaptiveCard()
    {
        var adaptiveCard = new { type = "AdaptiveCard", version = "1.2" };

        MessageActivity activity = (MessageActivity)messageBuilder
            .AddAdaptiveCardAttachment(adaptiveCard)
            .Build();

        Assert.NotNull(activity.Attachments);
        Assert.Single(activity.Attachments);
        Assert.Equal("application/vnd.microsoft.card.adaptive", activity.Attachments[0].ContentType);
        Assert.Same(adaptiveCard, activity.Attachments[0].Content);
    }

    [Fact]
    public void WithAdaptiveCardAttachment_ConfigureActionAppliesChanges()
    {
        var adaptiveCard = new { type = "AdaptiveCard" };

        MessageActivity activity = (MessageActivity)messageBuilder
            .WithAdaptiveCardAttachment(adaptiveCard, b => b.WithName("feedback"))
            .Build();

        Assert.NotNull(activity.Attachments);
        Assert.Single(activity.Attachments);
        Assert.Equal("feedback", activity.Attachments[0].Name);
    }

    [Fact]
    public void AddAdaptiveCardAttachment_WithNullPayload_Throws()
    {
        Assert.Throws<ArgumentNullException>(() => builder.AddAdaptiveCardAttachment(null!));
    }

    [Fact]
    public void AddMention_WithNullAccount_ThrowsArgumentNullException()
    {
        Assert.Throws<ArgumentNullException>(() => builder.AddMention(null!));
    }

    [Fact]
    public void AddMention_WithAccountAndDefaultText_AddsMentionAndUpdatesText()
    {
        ConversationAccount account = new()
        {
            Id = "user-123",
            Name = "John Doe"
        };

        TeamsActivity activity = builder
            .WithText("said hello")
            .AddMention(account)
            .Build();

        Assert.Equal("<at>John Doe</at> said hello", activity.Properties["text"]);
        Assert.NotNull(activity.Entities);
        Assert.Single(activity.Entities);

        MentionEntity? mention = activity.Entities[0] as MentionEntity;
        Assert.NotNull(mention);
        Assert.Equal("user-123", mention.Mentioned?.Id);
        Assert.Equal("John Doe", mention.Mentioned?.Name);
        Assert.Equal("<at>John Doe</at>", mention.Text);
    }

    [Fact]
    public void AddMention_WithCustomText_UsesCustomText()
    {
        ConversationAccount account = new()
        {
            Id = "user-123",
            Name = "John Doe"
        };

        TeamsActivity activity = builder
            .WithText("replied")
            .AddMention(account, "CustomName")
            .Build();

        Assert.Equal("<at>CustomName</at> replied", activity.Properties["text"]);

        MentionEntity? mention = activity.Entities![0] as MentionEntity;
        Assert.NotNull(mention);
        Assert.Equal("<at>CustomName</at>", mention.Text);
    }

    [Fact]
    public void AddMention_WithAddTextFalse_DoesNotUpdateText()
    {
        ConversationAccount account = new()
        {
            Id = "user-123",
            Name = "John Doe"
        };

        TeamsActivity activity = builder
            .WithText("original text")
            .AddMention(account, addText: false)
            .Build();

        Assert.Equal("original text", activity.Properties["text"]);
        Assert.NotNull(activity.Entities);
        Assert.Single(activity.Entities);
    }

    [Fact]
    public void AddMention_MultipleMentions_AddsAllMentions()
    {
        ConversationAccount account1 = new() { Id = "user-1", Name = "User One" };
        ConversationAccount account2 = new() { Id = "user-2", Name = "User Two" };

        TeamsActivity activity = builder
            .WithText("message")
            .AddMention(account1)
            .AddMention(account2)
            .Build();

        Assert.Equal("<at>User Two</at> <at>User One</at> message", activity.Properties["text"]);
        Assert.NotNull(activity.Entities);
        Assert.Equal(2, activity.Entities?.Count);
    }

    [Fact]
    public void FluentAPI_CompleteActivity_BuildsCorrectly()
    {
        MessageActivity activity = (MessageActivity)messageBuilder
            .WithType(TeamsActivityType.Message)
            .WithId("activity-123")
            .WithChannelId("msteams")
            .WithText("Test message")
            .WithServiceUrl(new Uri("https://smba.trafficmanager.net/teams/"))
            .WithFrom(TeamsConversationAccount.FromConversationAccount(new ConversationAccount
            {
                Id = "sender-id",
                Name = "Sender"
            }))
            .WithRecipient(TeamsConversationAccount.FromConversationAccount(new ConversationAccount
            {
                Id = "recipient-id",
                Name = "Recipient"
            }))
            .WithConversation(TeamsConversation.FromConversation(new Conversation
            {
                Id = "conv-id"
            }))
            .AddEntity(new ClientInfoEntity { Locale = "en-US" })
            .AddAttachment(new TeamsAttachment { ContentType = "text/html" })
            .AddMention(new ConversationAccount { Id = "user-1", Name = "User" })
            .Build();

        Assert.Equal(TeamsActivityType.Message, activity.Type);
        Assert.Equal("activity-123", activity.Id);
        Assert.Equal("msteams", activity.ChannelId);
        string? text = activity.Text;
        if (text is null && activity.Properties.TryGetValue("text", out object? rawText))
        {
            text = rawText?.ToString();
        }
        Assert.Equal("<at>User</at> Test message", text);
        Assert.Equal("sender-id", activity.From?.Id);
        Assert.Equal("recipient-id", activity.Recipient?.Id);
        Assert.Equal("conv-id", activity.Conversation?.Id);
        Assert.NotNull(activity.Entities);
        Assert.Equal(2, activity.Entities?.Count); // ClientInfo + Mention
        Assert.NotNull(activity.Attachments);
        Assert.Single(activity.Attachments);
    }

    [Fact]
    public void FluentAPI_MethodChaining_ReturnsBuilderInstance()
    {

        TeamsActivityBuilder result1 = builder.WithId("id");
        TeamsActivityBuilder result2 = builder.WithText("text");
        TeamsActivityBuilder result3 = builder.WithType(TeamsActivityType.Message);

        Assert.Same(builder, result1);
        Assert.Same(builder, result2);
        Assert.Same(builder, result3);
    }

    [Fact]
    public void Build_CalledMultipleTimes_ReturnsSameInstance()
    {
        builder
            .WithId("test-id");

        TeamsActivity activity1 = builder.Build();
        TeamsActivity activity2 = builder.Build();

        Assert.Same(activity1, activity2);
    }

    [Fact]
    public void Builder_ModifyingExistingActivity_PreservesOriginalData()
    {
        TeamsActivity original = new()
        {
            Id = "original-id",
            Type = TeamsActivityType.Message
        };
        original.Properties["text"] = "original text";

        TeamsActivity modified = TeamsActivity.CreateBuilder(original)
            .WithText("modified text")
            .Build();

        Assert.Equal("original-id", modified.Id);
        Assert.Equal("modified text", modified.Properties["text"]);
        Assert.Equal(TeamsActivityType.Message, modified.Type);
    }

    [Fact]
    public void AddMention_UpdatesBaseEntityCollection()
    {
        ConversationAccount account = new()
        {
            Id = "user-123",
            Name = "Test User"
        };

        TeamsActivity activity = builder
            .AddMention(account)
            .Build();

        // Entities are on TeamsActivity, not CoreActivity; verify via TeamsActivity
        Assert.NotNull(activity.Entities);
        Assert.NotEmpty(activity.Entities);
    }

    [Fact]
    public void WithChannelData_NullValue_SetsToNull()
    {
        TeamsActivity activity = builder
            .WithChannelData(null!)
            .Build();

        Assert.Null(activity.ChannelData);
    }

    [Fact]
    public void AddEntity_NullEntitiesCollection_InitializesCollection()
    {
        TeamsActivity activity = builder.Build();

        Assert.Null(activity.Entities);

        ClientInfoEntity entity = new() { Locale = "en-US" };
        builder.AddEntity(entity);

        TeamsActivity result = builder.Build();
        Assert.NotNull(result.Entities);
        Assert.Single(result.Entities);
    }

    [Fact]
    public void AddAttachment_NullAttachmentsCollection_InitializesCollection()
    {
        MessageActivity activity = (MessageActivity)messageBuilder.Build();

        Assert.Null(activity.Attachments);

        TeamsAttachment attachment = new() { ContentType = "text/html" };
        messageBuilder.AddAttachment(attachment);

        MessageActivity result = (MessageActivity)messageBuilder.Build();
        Assert.NotNull(result.Attachments);
        Assert.Single(result.Attachments);
    }

    [Fact]
    public void Builder_EmptyText_AddMention_PrependsMention()
    {
        ConversationAccount account = new()
        {
            Id = "user-123",
            Name = "User"
        };

        TeamsActivity activity = builder
            .AddMention(account)
            .Build();

        Assert.Equal("<at>User</at> ", activity.Properties["text"]);
    }

    [Fact]
    public void WithConversationReference_WithNullActivity_ThrowsArgumentNullException()
    {
        Assert.Throws<ArgumentNullException>(() => builder.WithConversationReference(null!));
    }

    [Fact]
    public void WithConversationReference_WithNullChannelId_ThrowsArgumentNullException()
    {

        TeamsActivity sourceActivity = new()
        {
            ChannelId = null!,
            ServiceUrl = new Uri("https://test.com"),
            Conversation = TeamsConversation.FromConversation(new Conversation()),
            From = TeamsConversationAccount.FromConversationAccount(new ConversationAccount()),
            Recipient = TeamsConversationAccount.FromConversationAccount(new ConversationAccount())
        };

        Assert.Throws<ArgumentNullException>(() => builder.WithConversationReference(sourceActivity));
    }

    [Fact]
    public void WithConversationReference_WithNullServiceUrl_ThrowsArgumentNullException()
    {
        TeamsActivity sourceActivity = new()
        {
            ChannelId = "msteams",
            ServiceUrl = null!,
            Conversation = TeamsConversation.FromConversation(new Conversation()),
            From = TeamsConversationAccount.FromConversationAccount(new ConversationAccount()),
            Recipient = TeamsConversationAccount.FromConversationAccount(new ConversationAccount())
        };

        Assert.Throws<ArgumentNullException>(() => builder.WithConversationReference(sourceActivity));
    }

    [Fact]
    public void WithConversationReference_WithEmptyConversationId_DoesNotThrow()
    {
        TeamsActivity sourceActivity = new()
        {
            ChannelId = "msteams",
            ServiceUrl = new Uri("https://test.com"),
            Conversation = TeamsConversation.FromConversation(new Conversation()),
            From = TeamsConversationAccount.FromConversationAccount(new ConversationAccount { Id = "user-1" }),
            Recipient = TeamsConversationAccount.FromConversationAccount(new ConversationAccount { Id = "bot-1" })
        };

        TeamsActivity result = builder.WithConversationReference(sourceActivity).Build();

        Assert.NotNull(result.Conversation);
    }

    [Fact]
    public void WithConversationReference_WithEmptyFromId_DoesNotThrow()
    {
        TeamsActivity sourceActivity = new()
        {
            ChannelId = "msteams",
            ServiceUrl = new Uri("https://test.com"),
            Conversation = TeamsConversation.FromConversation(new Conversation { Id = "conv-1" }),
            From = TeamsConversationAccount.FromConversationAccount(new ConversationAccount()),
            Recipient = TeamsConversationAccount.FromConversationAccount(new ConversationAccount { Id = "bot-1" })
        };

        TeamsActivity result = builder.WithConversationReference(sourceActivity).Build();

        Assert.NotNull(result.From);
    }

    [Fact]
    public void WithConversationReference_WithEmptyRecipientId_DoesNotThrow()
    {
        TeamsActivity sourceActivity = new()
        {
            ChannelId = "msteams",
            ServiceUrl = new Uri("https://test.com"),
            Conversation = TeamsConversation.FromConversation(new Conversation { Id = "conv-1" }),
            From = TeamsConversationAccount.FromConversationAccount(new ConversationAccount { Id = "user-1" }),
            Recipient = TeamsConversationAccount.FromConversationAccount(new ConversationAccount())
        };

        TeamsActivity result = builder.WithConversationReference(sourceActivity).Build();

        Assert.NotNull(result.From);
    }

    [Fact]
    public void WithFrom_WithBaseConversationAccount_ConvertsToTeamsConversationAccount()
    {
        ConversationAccount baseAccount = new()
        {
            Id = "user-123",
            Name = "User Name"
        };

        TeamsActivity activity = builder
            .WithFrom(baseAccount)
            .Build();

        Assert.IsType<TeamsConversationAccount>(activity.From);
        Assert.Equal("user-123", activity.From?.Id);
        Assert.Equal("User Name", activity.From?.Name);
    }

    [Fact]
    public void WithRecipient_WithBaseConversationAccount_ConvertsToTeamsConversationAccount()
    {
        ConversationAccount baseAccount = new()
        {
            Id = "bot-123",
            Name = "Bot Name"
        };

        TeamsActivity activity = builder
            .WithRecipient(baseAccount)
            .Build();

        Assert.IsType<TeamsConversationAccount>(activity.Recipient);
        Assert.Equal("bot-123", activity.Recipient?.Id);
        Assert.Equal("Bot Name", activity.Recipient?.Name);
    }

    [Fact]
    public void WithConversation_WithBaseConversation_ConvertsToTeamsConversation()
    {
        Conversation baseConversation = new()
        {
            Id = "conv-123"
        };

        TeamsActivity activity = builder
            .WithConversation(baseConversation)
            .Build();

        Assert.IsType<TeamsConversation>(activity.Conversation);
        Assert.Equal("conv-123", activity.Conversation?.Id);
    }

    [Fact]
    public void WithEntities_WithNullValue_SetsToNull()
    {
        TeamsActivity activity = builder
            .WithEntities([new ClientInfoEntity()])
            .WithEntities(null!)
            .Build();

        Assert.Null(activity.Entities);
    }

    [Fact]
    public void WithAttachments_WithNullValue_SetsToNull()
    {
        MessageActivity activity = (MessageActivity)messageBuilder
            .WithAttachments([new()])
            .WithAttachments(null!)
            .Build();

        Assert.Null(activity.Attachments);
    }

    [Fact]
    public void AddMention_WithAccountWithNullName_UsesNullText()
    {
        ConversationAccount account = new()
        {
            Id = "user-123",
            Name = null
        };

        TeamsActivity activity = builder
            .WithText("message")
            .AddMention(account)
            .Build();

        Assert.Equal("<at></at> message", activity.Properties["text"]);
        Assert.NotNull(activity.Entities);
        Assert.Single(activity.Entities);
    }

    [Fact]
    public void Build_MultipleCalls_ReturnsRebasedActivity()
    {
        builder
            .AddEntity(new ClientInfoEntity { Locale = "en-US" });

        TeamsActivity activity1 = builder.Build();
        Assert.NotNull(activity1.Entities);

        builder.AddEntity(new ProductInfoEntity { Id = "prod-1" });
        TeamsActivity activity2 = builder.Build();

        Assert.Same(activity1, activity2);
        Assert.NotNull(activity2.Entities);
        Assert.Equal(2, activity2.Entities!.Count);
    }

    [Fact]
    public void IntegrationTest_CreateComplexActivity()
    {
        Uri serviceUrl = new("https://smba.trafficmanager.net/amer/test/");
        TeamsChannelData channelData = new()
        {
            TeamsChannelId = "19:channel@thread.tacv2",
            TeamsTeamId = "19:team@thread.tacv2"
        };

        Conversation conv = new()
        {
            Id = "conv-001",
            Properties =
            {
                { "tenantId", "tenant-001" },
                { "conversationType", "channel" }
            }
        };

        TeamsConversation? tc = TeamsConversation.FromConversation(conv);
        Assert.NotNull(tc);

        MessageActivity activity = (MessageActivity)messageBuilder
            .WithType(TeamsActivityType.Message)
            .WithId("msg-001")
            .WithServiceUrl(serviceUrl)
            .WithChannelId("msteams")
            .WithText("Please review this document")
            .WithFrom(TeamsConversationAccount.FromConversationAccount(new ConversationAccount
            {
                Id = "bot-id",
                Name = "Bot"
            }))
            .WithRecipient(TeamsConversationAccount.FromConversationAccount(new ConversationAccount
            {
                Id = "user-id",
                Name = "User"
            }))
            .WithConversation(tc)
            .WithChannelData(channelData)
            .AddEntity(new ClientInfoEntity
            {
                Locale = "en-US",
                Country = "US",
                Platform = "Web"
            })
            .AddAttachment(new TeamsAttachment
            {
                ContentType = "application/vnd.microsoft.card.adaptive",
                Name = "adaptive-card.json"
            })
            .AddMention(new ConversationAccount
            {
                Id = "manager-id",
                Name = "Manager"
            }, "Manager")
            .Build();

        // Verify all properties
        Assert.Equal(TeamsActivityType.Message, activity.Type);
        Assert.Equal("msg-001", activity.Id);
        Assert.Equal(serviceUrl, activity.ServiceUrl);
        Assert.Equal("msteams", activity.ChannelId);
        string? text = activity.Text;
        if (text is null && activity.Properties.TryGetValue("text", out object? rawText))
        {
            text = rawText?.ToString();
        }
        Assert.Equal("<at>Manager</at> Please review this document", text);
        Assert.Equal("bot-id", activity.From?.Id);
        Assert.Equal("user-id", activity.Recipient?.Id);
        Assert.Equal("conv-001", activity.Conversation?.Id);
        Assert.Equal("tenant-001", activity.Conversation?.TenantId);
        Assert.Equal("channel", activity.Conversation?.ConversationType);
        Assert.NotNull(activity.ChannelData);
        Assert.Equal("19:channel@thread.tacv2", activity.ChannelData?.TeamsChannelId);
        Assert.NotNull(activity.Entities);
        Assert.Equal(2, activity.Entities?.Count); // ClientInfo + Mention
        Assert.NotNull(activity.Attachments);
        Assert.Single(activity.Attachments);
    }

    private const string json = """
                {
                    "type": "message",
                    "channelId": "msteams",
                    "serviceUrl": "https://smba.trafficmanager.net/amer/test/",
                    "text": "hello",
                    "from": {
                        "id": "user-1",
                        "name": "User One"
                    },
                    "recipient": {
                        "id": "bot-1",
                        "name": "Bot One"
                    },
                    "conversation": {
                        "id": "conv-1",
                        "tenantId": "tenant-1"
                    }
                }
                """;
}
