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

using System.Diagnostics.CodeAnalysis;
using System.Text.Json.Serialization;

using Microsoft.Teams.Api.Entities;
using Microsoft.Teams.Common;

namespace Microsoft.Teams.Api.Activities;

public partial class ActivityType : StringEnum
{
    public static readonly ActivityType Message = new("message");
    public bool IsMessage => Message.Equals(Value);
}

public class MessageActivity : Activity
{
    [JsonPropertyName("text")]
    [JsonPropertyOrder(31)]
    public string Text { get; set; }

    [JsonPropertyName("speak")]
    [JsonPropertyOrder(32)]
    public string? Speak { get; set; }

    [JsonPropertyName("inputHint")]
    [JsonPropertyOrder(33)]
    public InputHint? InputHint { get; set; }

    [JsonPropertyName("summary")]
    [JsonPropertyOrder(34)]
    public string? Summary { get; set; }

    [JsonPropertyName("textFormat")]
    [JsonPropertyOrder(35)]
    public TextFormat? TextFormat { get; set; }

    [JsonPropertyName("attachmentLayout")]
    [JsonPropertyOrder(121)]
    public Attachment.Layout? AttachmentLayout { get; set; }

    [JsonPropertyName("attachments")]
    [JsonPropertyOrder(122)]
    public IList<Attachment>? Attachments { get; set; }

    [JsonPropertyName("suggestedActions")]
    [JsonPropertyOrder(123)]
    public SuggestedActions? SuggestedActions { get; set; }

    [JsonPropertyName("importance")]
    [JsonPropertyOrder(39)]
    public Importance? Importance { get; set; }

    [JsonPropertyName("deliveryMode")]
    [JsonPropertyOrder(41)]
    public DeliveryMode? DeliveryMode { get; set; }

    [JsonPropertyName("expiration")]
    [JsonPropertyOrder(42)]
    public DateTime? Expiration { get; set; }

    [JsonPropertyName("value")]
    [JsonPropertyOrder(43)]
    public object? Value { get; set; }

    [JsonIgnore]
    public bool IsRecipientMentioned
    {
        get => (Entities ?? []).Any(e => e is MentionEntity mention && mention.Mentioned.Id == Recipient.Id);
    }
    /// <summary>
    /// Get all quoted reply entities from this message.
    /// </summary>
    [Experimental("ExperimentalTeamsQuotedReplies")]
#pragma warning disable ExperimentalTeamsQuotedReplies
    public IReadOnlyList<QuotedReplyEntity> GetQuotedMessages()
    {
        return Entities?.OfType<QuotedReplyEntity>().ToList()
            ?? new List<QuotedReplyEntity>();
    }
#pragma warning restore ExperimentalTeamsQuotedReplies

    public MessageActivity() : base(ActivityType.Message)
    {
        Text ??= string.Empty;
    }

    public MessageActivity(string text) : base(ActivityType.Message)
    {
        Text = text;
    }

    public MessageActivity WithText(string text)
    {
        Text = text;
        return this;
    }

    public MessageActivity WithSpeak(string speak)
    {
        Speak = speak;
        return this;
    }

    public MessageActivity WithInputHint(InputHint inputHint)
    {
        InputHint = inputHint;
        return this;
    }

    public MessageActivity WithSummary(string summary)
    {
        Summary = summary;
        return this;
    }

    public MessageActivity WithTextFormat(TextFormat textFormat)
    {
        TextFormat = textFormat;
        return this;
    }

    public MessageActivity WithAttachmentLayout(Attachment.Layout attachmentLayout)
    {
        AttachmentLayout = attachmentLayout;
        return this;
    }

    public MessageActivity WithSuggestedActions(SuggestedActions suggestedActions)
    {
        SuggestedActions = suggestedActions;
        return this;
    }

    public MessageActivity WithImportance(Importance importance)
    {
        Importance = importance;
        return this;
    }

    public MessageActivity WithDeliveryMode(DeliveryMode deliveryMode)
    {
        DeliveryMode = deliveryMode;
        return this;
    }

    public MessageActivity WithExpiration(DateTime expiration)
    {
        Expiration = expiration;
        return this;
    }

    public MessageActivity AddText(string text)
    {
        Text += text;
        return this;
    }

    public override MessageActivity WithRecipient(Account value)
    {
        return (MessageActivity)base.WithRecipient(value);
    }

    [Experimental("ExperimentalTeamsTargeted")]
#pragma warning disable ExperimentalTeamsTargeted
    public override MessageActivity WithRecipient(Account value, bool isTargeted = false)
    {
        return (MessageActivity)base.WithRecipient(value, isTargeted);
    }
#pragma warning restore ExperimentalTeamsTargeted

    /// <summary>
    /// Add a quoted message reference and append a placeholder to text.
    /// Teams renders the quoted message as a preview bubble above the response text.
    /// If text is provided, it is appended to the quoted message placeholder.
    /// </summary>
    /// <param name="messageId">the ID of the message to quote</param>
    /// <param name="text">optional text, appended to the quoted message placeholder</param>
    [Experimental("ExperimentalTeamsQuotedReplies")]
#pragma warning disable ExperimentalTeamsQuotedReplies
    public MessageActivity AddQuote(string messageId, string? text = null)
    {
        Entities ??= new List<IEntity>();
        Entities.Add(new QuotedReplyEntity
        {
            QuotedReply = new QuotedReplyData { MessageId = messageId }
        });
        AddText($"<quoted messageId=\"{messageId}\"/>");
        if (text != null)
        {
            AddText($" {text}");
        }
        return this;
    }
#pragma warning restore ExperimentalTeamsQuotedReplies

    /// <summary>
    /// Prepend a QuotedReply entity and placeholder before existing text.
    /// Used by Reply()/Quote() for quote-above-response.
    /// </summary>
    [Experimental("ExperimentalTeamsQuotedReplies")]
#pragma warning disable ExperimentalTeamsQuotedReplies
    public MessageActivity PrependQuote(string messageId)
    {
        Entities ??= new List<IEntity>();
        Entities.Add(new QuotedReplyEntity
        {
            QuotedReply = new QuotedReplyData { MessageId = messageId }
        });
        var placeholder = $"<quoted messageId=\"{messageId}\"/>";
        var hasText = !string.IsNullOrWhiteSpace(Text);
        Text = hasText ? $"{placeholder} {Text}" : placeholder;
        return this;
    }
#pragma warning restore ExperimentalTeamsQuotedReplies


    public MessageActivity AddAttachment(params Attachment[] value)
    {
        Attachments ??= [];

        foreach (var attachment in value)
        {
            Attachments.Add(attachment);
        }

        return this;
    }

    public MessageActivity AddAttachment(Teams.Cards.AdaptiveCard card)
    {
        return AddAttachment(new Attachment(card));
    }

    public MessageActivity AddAttachment(Cards.OAuthCard card)
    {
        return AddAttachment(new Attachment(card));
    }

    public MessageActivity AddAttachment(Cards.SignInCard card)
    {
        return AddAttachment(new Attachment(card));
    }

    public MessageActivity AddMention(Account account, string? text = null, bool addText = true)
    {
        var mentionText = text ?? account.Name;

        if (addText)
        {
            Text = $"<at>{mentionText}</at> {Text}";
        }

        AddEntity(new MentionEntity()
        {
            Mentioned = account,
            Text = $"<at>{mentionText}</at>"
        });

        return this;
    }

    public MessageActivity AddStreamFinal()
    {
        ChannelData ??= new();
        ChannelData.StreamId ??= Id;
        ChannelData.StreamType = StreamType.Final;

        AddEntity(new StreamInfoEntity()
        {
            StreamId = Id,
            StreamType = StreamType.Final
        });

        return this;
    }

    public MentionEntity? GetAccountMention(string accountId)
    {
        return (MentionEntity?)(Entities ?? []).FirstOrDefault(e => e is MentionEntity mention && mention.Mentioned.Id == accountId);
    }

    public MessageActivity Merge(MessageActivity from)
    {
        base.Merge(from);

        Text ??= from.Text;
        Speak ??= from.Speak;
        InputHint ??= from.InputHint;
        Summary ??= from.Summary;
        TextFormat ??= from.TextFormat;
        AttachmentLayout ??= from.AttachmentLayout;
        SuggestedActions ??= from.SuggestedActions;
        Importance ??= from.Importance;
        DeliveryMode ??= from.DeliveryMode;
        Expiration ??= from.Expiration;
        Value ??= from.Value;
        AddAttachment(from.Attachments?.ToArray() ?? []);

        return this;
    }
}