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

using Microsoft.Teams.Apps.Handlers;
using Microsoft.Teams.Apps.Schema;
using Microsoft.Teams.Apps.Schema.Entities;
using Microsoft.Teams.Core.Schema;
namespace Microsoft.Teams.Apps.UnitTests;

public class TeamsActivityTests
{
    [Fact]
    public void DownCastTeamsActivity_To_CoreActivity()
    {
        CoreActivity activity = CoreActivity.FromJsonString(json);
        Assert.NotNull(activity.Conversation);
        Assert.Equal("19:6848757105754c8981c67612732d9aa7@thread.tacv2;messageid=1759881511856", activity.Conversation.Id);
        TeamsActivity teamsActivity = TeamsActivity.FromActivity(activity);
        Assert.Equal("19:6848757105754c8981c67612732d9aa7@thread.tacv2;messageid=1759881511856", teamsActivity.Conversation!.Id);

    }

    [Fact]
    public void DownCastTeamsActivity_To_CoreActivity_FromBuilder()
    {

        TeamsActivity teamsActivity = TeamsActivity
            .CreateBuilder()
            .WithConversation(new Conversation() { Id = "19:6848757105754c8981c67612732d9aa7@thread.tacv2;messageid=1759881511856" })
            .Build();

        static void AssertCid(CoreActivity a)
        {
            Assert.IsAssignableFrom<TeamsActivity>(a);
            Assert.Equal("19:6848757105754c8981c67612732d9aa7@thread.tacv2;messageid=1759881511856", ((TeamsActivity)a).Conversation!.Id);
        }
        AssertCid(teamsActivity);
    }

    [Fact]
    public void DownCastTeamsActivity_To_CoreActivity_WithoutRebase()
    {
        TeamsActivity teamsActivity = new()
        {
            Conversation = new TeamsConversation()
            {
                Id = "19:6848757105754c8981c67612732d9aa7@thread.tacv2;messageid=1759881511856"
            }
        };
        Assert.Equal("19:6848757105754c8981c67612732d9aa7@thread.tacv2;messageid=1759881511856", teamsActivity.Conversation!.Id);

        static void AssertCid(CoreActivity a)
        {
            Assert.IsAssignableFrom<TeamsActivity>(a);
            Assert.Equal("19:6848757105754c8981c67612732d9aa7@thread.tacv2;messageid=1759881511856", ((TeamsActivity)a).Conversation!.Id);
        }
        AssertCid(teamsActivity);

    }


    [Fact]
    public void AddMentionEntity_To_TeamsActivity()
    {
        TeamsActivity activity = TeamsActivity.FromActivity(new CoreActivity(ActivityType.Message));
        activity
            .AddMention(new ConversationAccount
            {
                Id = "user-id-01",
                Name = "rido"
            }, "ridotest");



        Assert.NotNull(activity.Entities);
        Assert.Single(activity.Entities);
        Assert.Equal("mention", activity.Entities[0].Type);
        MentionEntity? mention = activity.Entities[0] as MentionEntity;
        Assert.NotNull(mention);
        Assert.Equal("user-id-01", mention.Mentioned?.Id);
        Assert.Equal("rido", mention.Mentioned?.Name);
        Assert.Equal("<at>ridotest</at>", mention.Text);

        string jsonResult = activity.ToJson();
        Assert.Contains("user-id-01", jsonResult);
    }

    [Fact]
    public void AddMentionEntity_Serialize_From_CoreActivity()
    {
        TeamsActivity activity = TeamsActivity.FromActivity(new CoreActivity(ActivityType.Message));
        activity.AddMention(new ConversationAccount
        {
            Id = "user-id-01",
            Name = "rido"
        }, "ridotest");



        Assert.NotNull(activity.Entities);
        Assert.Single(activity.Entities);
        Assert.Equal("mention", activity.Entities[0].Type);
        MentionEntity? mention = activity.Entities[0] as MentionEntity;
        Assert.NotNull(mention);
        Assert.Equal("user-id-01", mention.Mentioned?.Id);
        Assert.Equal("rido", mention.Mentioned?.Name);
        Assert.Equal("<at>ridotest</at>", mention.Text);

        static void SerializeAndAssert(CoreActivity a)
        {
            string json = a.ToJson();
            Assert.Contains("user-id-01", json);
        }

        SerializeAndAssert(activity);
    }


    [Fact]
    public void TeamsActivityBuilder_FluentAPI()
    {
        TeamsActivity activity = TeamsActivity.CreateBuilder()
            .WithType(TeamsActivityType.Message)
            .WithText("Hello World")
            .WithChannelId("msteams")
            .AddMention(new ConversationAccount
            {
                Id = "user-123",
                Name = "TestUser"
            })
            .Build();

        Assert.Equal(ActivityType.Message, activity.Type);
        Assert.Equal("<at>TestUser</at> Hello World", activity.Properties["text"]);
        Assert.Equal("msteams", activity.ChannelId);
        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("TestUser", mention.Mentioned?.Name);
    }

    [Fact]
    public void Serialize_TeamsActivity_WithEntities()
    {
        TeamsActivity activity = TeamsActivity.CreateBuilder()
            .WithType(ActivityType.Message)
            .WithText("Hello World")
            .WithChannelId("msteams")
            .Build();

        activity.AddClientInfo("Web", "US", "America/Los_Angeles", "en-US");

        string jsonResult = activity.ToJson();
        Assert.Contains("clientInfo", jsonResult);
        Assert.Contains("Web", jsonResult);
        Assert.Contains("Hello World", jsonResult);
    }

    [Fact]
    public void Deserialize_TeamsActivity_Invoke_WithValue()
    {
        //TeamsActivity activity = CoreActivity.FromJsonString<TeamsActivity>(jsonInvoke);
        TeamsActivity activity = TeamsActivity.FromActivity(CoreActivity.FromJsonString(jsonInvoke));
        InvokeActivity invokeActivity = Assert.IsType<InvokeActivity>(activity);
        Assert.NotNull(invokeActivity.Value);
        string feedback = invokeActivity.Value?["action"]?["data"]?["feedback"]?.ToString()!;
        Assert.Equal("test invokes", feedback);
    }

    [Fact]
    public void Serialize_Does_Not_Repeat_AAdObjectId()
    {
        CoreActivity coreActivity = CoreActivity.FromJsonString("""
            {
                "type": "message",
                "recipient": {
                    "id": "rec1",
                    "name": "recname",
                    "aadObjectId": "rec-aadId-1"
                }
            }
            """);
        TeamsActivity teamsActivity = TeamsActivity.FromActivity(coreActivity);
        string json = teamsActivity.ToJson();
        string[] found = json.Split("aadObjectId");
        Assert.Equal(1, found.Length - 1); // only one occurrence
    }

    [Fact]
    public void FromActivity_Overrides_Recipient()
    {
        CoreActivity coreActivity = CoreActivity.FromJsonString("""
            {
                "type": "message",
                "recipient": {
                    "id": "rec1",
                    "name": "recname",
                    "agenticUserId": "0d5eb8a3-1642-4e63-9ccc-a89aa461716c",
                    "agenticAppId": "3fc62d4f-b04e-4c71-878b-02a2fa395fe2",
                    "agenticAppBlueprintId": "24fff850-d7fb-4d32-a6e7-a1178874430e"
                }
            }
            """);
        TeamsActivity teamsActivity = TeamsActivity.FromActivity(coreActivity);
        Assert.Equal("rec1", teamsActivity.Recipient?.Id);
        Assert.Equal("recname", teamsActivity.Recipient?.Name);
        AgenticIdentity? agenticIdentity = AgenticIdentity.FromAccount(teamsActivity.Recipient);
        Assert.NotNull(agenticIdentity);
        Assert.Equal("0d5eb8a3-1642-4e63-9ccc-a89aa461716c", agenticIdentity.AgenticUserId);
        Assert.Equal("3fc62d4f-b04e-4c71-878b-02a2fa395fe2", agenticIdentity.AgenticAppId);
        Assert.Equal("24fff850-d7fb-4d32-a6e7-a1178874430e", agenticIdentity.AgenticAppBlueprintId);
    }

    [Fact]
    public void MessageActivity_FromActivity_PreservesFromAndRecipient()
    {
        CoreActivity coreActivity = CoreActivity.FromJsonString("""
            {
                "type": "message",
                "text": "hello",
                "from": {
                    "id": "user1",
                    "name": "User One",
                    "agenticAppId": "app-1"
                },
                "recipient": {
                    "id": "bot1",
                    "name": "Bot One"
                }
            }
            """);

        MessageActivity messageActivity = MessageActivity.FromActivity(coreActivity);

        Assert.Equal("hello", messageActivity.Text);
        Assert.NotNull(messageActivity.From);
        Assert.Equal("user1", messageActivity.From.Id);
        Assert.Equal("User One", messageActivity.From.Name);
        Assert.Equal("app-1", messageActivity.From.AgenticAppId);
        Assert.NotNull(messageActivity.Recipient);
        Assert.Equal("bot1", messageActivity.Recipient.Id);
        Assert.Equal("Bot One", messageActivity.Recipient.Name);
    }

    [Fact]
    public void FromActivity_ReturnsDerivedType_WhenRegistered()
    {
        CoreActivity coreActivity = new(ActivityType.Message);
        TeamsActivity activity = TeamsActivity.FromActivity(coreActivity);

        Assert.IsType<MessageActivity>(activity);
    }

    [Fact]
    public void FromActivity_ReturnsBaseType_WhenNotRegistered()
    {
        CoreActivity coreActivity = new("unknownType");
        TeamsActivity activity = TeamsActivity.FromActivity(coreActivity);

        Assert.Equal(typeof(TeamsActivity), activity.GetType());
        Assert.Equal("unknownType", activity.Type);
    }

    [Fact]
    public void EmptyTeamsActivity()
    {
        string minActivityJson = """
            {
              "type": "message"
            }
            """;

        TeamsActivity teamsActivity = TeamsActivity.CreateBuilder().Build();
        Assert.NotNull(teamsActivity);
        string json = teamsActivity.ToJson();
        Assert.Equal(minActivityJson, json);
    }

    [Fact]
    public void BaseFieldsAsBaseTypes()
    {
        CoreActivity ca = CoreActivity.FromJsonString("""
            {
                "type": "message",
                "conversation": { "id": "conv1", "tenantId": "tenant-1" }
            }
            """);
        TeamsActivity ta = TeamsActivity.FromActivity(ca);
        if (ta.Conversation is not null)
        {
            Assert.NotNull(ta.Conversation);
            Assert.Equal("conv1", ta.Conversation.Id);
            Assert.Empty(ta.Conversation.Properties);
        }
        else
        {
            Assert.Fail("Conversation not set");
        }
    }

    [Fact]
    public void Deserialize_with_Conversation_and_Tenant()
    {
        string json = """
            {
                "type" : "message",
                "conversation": {
                    "id" : "conv1",
                    "tenantId" : "tenant-1"
                }
            }
            """;
        CoreActivity ca = CoreActivity.FromJsonString(json);
        Assert.NotNull(ca);
        Assert.NotNull(ca.Conversation);
        Assert.Equal("conv1", ca.Conversation.Id);
        string caJson = ca.ToJson();
        Assert.Contains("\"conversation\"", caJson);
        Assert.Contains("\"conv1\"", caJson);
        Assert.Contains("\"tenant-1\"", caJson);
        TeamsActivity ta = TeamsActivity.FromActivity(ca);
        Assert.NotNull(ta);
        Assert.NotNull(ta.Conversation);
        Assert.Equal("conv1", ta.Conversation.Id);
        Assert.Equal("tenant-1", ta.Conversation.TenantId);
    }


    [Fact]
    public void TeamsActivityBuilder_WithFrom_SyncsBaseProperty()
    {
        // Verify that From/Recipient set via builder are accessible through a CoreActivity reference
        TeamsActivity incoming = TeamsActivity.FromActivity(CoreActivity.FromJsonString(json));
        TeamsActivity reply = TeamsActivity.CreateBuilder()
            .WithConversationReference(incoming)
            .WithText("test")
            .Build();

        // Access through CoreActivity reference (as ConversationClient would)
        CoreActivity coreRef = reply;
        Assert.NotNull(coreRef.From);
        Assert.Equal(incoming.Recipient?.Id, coreRef.From.Id);

        // AgenticIdentity should be accessible through the base From
        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 TeamsActivityBuilder_WithFrom_DoesNotProduceDuplicateFromInJson()
    {
        // Build a TeamsActivity with WithConversationReference which calls WithFrom
        TeamsActivity incoming = TeamsActivity.FromActivity(CoreActivity.FromJsonString(json));
        TeamsActivity reply = TeamsActivity.CreateBuilder()
            .WithConversationReference(incoming)
            .WithText("hello")
            .Build();

        string serialized = reply.ToJson();

        // Count occurrences of "from" key in the JSON — should appear exactly once
        int fromCount = System.Text.RegularExpressions.Regex.Matches(serialized, "\"from\"\\s*:").Count;
        Assert.Equal(1, fromCount);
    }

    [Fact]
    public void TeamsActivityBuilder_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);
    }

    private const string jsonInvoke = """
          {
          "type": "invoke",
          "channelId": "msteams",
          "id": "f:17b96347-e8b4-f340-10bc-eb52fc1a6ad4",
          "serviceUrl": "https://smba.trafficmanager.net/amer/56653e9d-2158-46ee-90d7-675c39642038/",
          "channelData": {
            "tenant": {
              "id": "56653e9d-2158-46ee-90d7-675c39642038"
            },
            "source": {
              "name": "message"
            },
            "legacy": {
              "replyToId": "1:12SWreU4430kJA9eZCb1kXDuo6A8KdDEGB6d9TkjuDYM"
            }
          },
          "from": {
            "id": "29:1uMVvhoAyfTqdMsyvHL0qlJTTfQF9MOUSI8_cQts2kdSWEZVDyJO2jz-CsNOhQcdYq1Bw4cHT0__O6XDj4AZ-Jw",
            "name": "Rido",
            "aadObjectId": "c5e99701-2a32-49c1-a660-4629ceeb8c61"
          },
          "recipient": {
            "id": "28:aabdbd62-bc97-4afb-83ee-575594577de5",
            "name": "ridobotlocal"
          },
          "conversation": {
            "id": "a:17vxw6pGQOb3Zfh8acXT8m_PqHycYpaFgzu2mFMUfkT-h0UskMctq5ZPPc7FIQxn2bx7rBSm5yE_HeUXsCcKZBrv77RgorB3_1_pAdvMhi39ClxQgawzyQ9GBFkdiwOxT",
            "conversationType": "personal",
            "tenantId": "56653e9d-2158-46ee-90d7-675c39642038"
          },
          "entities": [
            {
              "locale": "en-US",
              "country": "US",
              "platform": "Web",
              "timezone": "America/Los_Angeles",
              "type": "clientInfo"
            }
          ],
          "value": {
            "action": {
              "type": "Action.Execute",
              "title": "Submit Feedback",
              "data": {
                "feedback": "test invokes"
              }
            },
            "trigger": "manual"
          },
          "name": "adaptiveCard/action",
          "timestamp": "2026-01-07T06:04:59.89Z",
          "localTimestamp": "2026-01-06T22:04:59.89-08:00",
          "replyToId": "1767765488332",
          "locale": "en-US",
          "localTimezone": "America/Los_Angeles"
        }
        """;

    private const string json = """
            {
              "type": "message",
              "channelId": "msteams",
              "text": "\u003Cat\u003Eridotest\u003C/at\u003E reply to thread",
              "id": "1759944781430",
              "serviceUrl": "https://smba.trafficmanager.net/amer/50612dbb-0237-4969-b378-8d42590f9c00/",
              "channelData": {
                "teamsChannelId": "19:6848757105754c8981c67612732d9aa7@thread.tacv2",
                "teamsTeamId": "19:66P469zibfbsGI-_a0aN_toLTZpyzS6u7CT3TsXdgPw1@thread.tacv2",
                "channel": {
                  "id": "19:6848757105754c8981c67612732d9aa7@thread.tacv2"
                },
                "team": {
                  "id": "19:66P469zibfbsGI-_a0aN_toLTZpyzS6u7CT3TsXdgPw1@thread.tacv2"
                },
                "tenant": {
                  "id": "50612dbb-0237-4969-b378-8d42590f9c00"
                }
              },
              "from": {
                "id": "29:17bUvCasIPKfQIXHvNzcPjD86fwm6GkWc1PvCGP2-NSkNb7AyGYpjQ7Xw-XgTwaHW5JxZ4KMNDxn1kcL8fwX1Nw",
                "name": "rido",
                "aadObjectId": "b15a9416-0ad3-4172-9210-7beb711d3f70"
              },
              "recipient": {
                "id": "28:0b6fe6d1-fece-44f7-9a48-56465e2d5ab8",
                "name": "ridotest"
              },
              "conversation": {
                "id": "19:6848757105754c8981c67612732d9aa7@thread.tacv2;messageid=1759881511856",
                "isGroup": true,
                "conversationType": "channel",
                "tenantId": "50612dbb-0237-4969-b378-8d42590f9c00"
              },
              "entities": [
                {
                  "mentioned": {
                    "id": "28:0b6fe6d1-fece-44f7-9a48-56465e2d5ab8",
                    "name": "ridotest"
                  },
                  "text": "\u003Cat\u003Eridotest\u003C/at\u003E",
                  "type": "mention"
                },
                {
                  "locale": "en-US",
                  "country": "US",
                  "platform": "Web",
                  "timezone": "America/Los_Angeles",
                  "type": "clientInfo"
                }
              ],
              "textFormat": "plain",
              "attachments": [
                {
                  "contentType": "text/html",
                  "content": "\u003Cp\u003E\u003Cspan itemtype=\u0022http://schema.skype.com/Mention\u0022 itemscope=\u0022\u0022 itemid=\u00220\u0022\u003Eridotest\u003C/span\u003E\u0026nbsp;reply to thread\u003C/p\u003E"
                }
              ],
              "timestamp": "2025-10-08T17:33:01.4953744Z",
              "localTimestamp": "2025-10-08T10:33:01.4953744-07:00",
              "locale": "en-US",
              "localTimezone": "America/Los_Angeles"
            }
            """;


}
