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

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

namespace Microsoft.Teams.Apps.Schema.Entities;

/// <summary>
/// Citation entity representing a message with citation claims.
/// </summary>
public class CitationEntity : OMessageEntity
{
    /// <summary>
    /// Creates a new instance of <see cref="CitationEntity"/>.
    /// </summary>
    public CitationEntity() : base()
    {
    }

    /// <summary>
    /// Creates a new instance of <see cref="CitationEntity"/> by copying data from an existing message entity.
    /// </summary>
    /// <param name="entity">The message entity to copy from. Cannot be null.</param>
    public CitationEntity(OMessageEntity entity) : base()
    {
        ArgumentNullException.ThrowIfNull(entity);
        OType = entity.OType;
        OContext = entity.OContext;
        Type = entity.Type;
        Properties = new Core.Schema.ExtendedPropertiesDictionary(entity.Properties);
        AdditionalType = entity.AdditionalType != null
            ? new List<string>(entity.AdditionalType)
            : null;
        if (entity is CitationEntity citationEntity)
        {
            Citation = citationEntity.Citation != null
                ? citationEntity.Citation.Select(c => new CitationClaim(c)).ToList()
                : null;
        }
    }

    /// <summary>
    /// Gets or sets the list of citation claims.
    /// </summary>
    [JsonPropertyName("citation")]
    public IList<CitationClaim>? Citation
    {
        get => base.Properties.Get<IList<CitationClaim>>("citation");
        set => base.Properties["citation"] = value;
    }
}

/// <summary>
/// Represents a citation claim with a position and appearance document.
/// </summary>
public class CitationClaim
{
    /// <summary>Initializes a new instance of <see cref="CitationClaim"/>.</summary>
    public CitationClaim() { }

    /// <summary>Creates a deep copy of <paramref name="other"/>.</summary>
    [SetsRequiredMembers]
    public CitationClaim(CitationClaim other)
    {
        ArgumentNullException.ThrowIfNull(other);
        Type = other.Type;
        Position = other.Position;
        Appearance = new CitationAppearanceDocument(other.Appearance);
    }

    /// <summary>
    /// Gets or sets the schema.org type. Always "Claim".
    /// </summary>
    [JsonPropertyName("@type")]
    public string Type { get; set; } = "Claim";

    /// <summary>
    /// Gets or sets the position of the citation in the message text.
    /// </summary>
    [JsonPropertyName("position")]
    public required int Position { get; set; }

    /// <summary>
    /// Gets or sets the appearance document describing the cited source.
    /// </summary>
    [JsonPropertyName("appearance")]
    public required CitationAppearanceDocument Appearance { get; set; }
}

/// <summary>
/// Represents the appearance of a cited document.
/// </summary>
public class CitationAppearanceDocument
{
    /// <summary>Initializes a new instance of <see cref="CitationAppearanceDocument"/>.</summary>
    public CitationAppearanceDocument() { }

    /// <summary>Creates a deep copy of <paramref name="other"/>.</summary>
    [SetsRequiredMembers]
    public CitationAppearanceDocument(CitationAppearanceDocument other)
    {
        ArgumentNullException.ThrowIfNull(other);
        Type = other.Type;
        Name = other.Name;
        Text = other.Text;
        Url = other.Url;
        Abstract = other.Abstract;
        EncodingFormat = other.EncodingFormat;
        Image = other.Image is null ? null : new CitationImageObject { Type = other.Image.Type, Name = other.Image.Name };
        Keywords = other.Keywords != null ? new List<string>(other.Keywords) : null;
        UsageInfo = other.UsageInfo;
    }

    /// <summary>
    /// Gets or sets the schema.org type. Always "DigitalDocument".
    /// </summary>
    [JsonPropertyName("@type")]
    public string Type { get; set; } = "DigitalDocument";

    /// <summary>
    /// Gets or sets the name of the document (max length 80).
    /// </summary>
    [JsonPropertyName("name")]
    public required string Name { get; set; }

    /// <summary>
    /// Gets or sets a stringified adaptive card with additional information about the citation.
    /// </summary>
    [JsonPropertyName("text")]
    public string? Text { get; set; }

    /// <summary>
    /// Gets or sets the URL of the document.
    /// </summary>
    [JsonPropertyName("url")]
    public Uri? Url { get; set; }

    /// <summary>
    /// Gets or sets the extract of the referenced content (max length 160).
    /// </summary>
    [JsonPropertyName("abstract")]
    public required string Abstract { get; set; }

    /// <summary>
    /// Gets or sets the encoding format of the text. See <see cref="EncodingFormats"/> for known values.
    /// </summary>
    [JsonPropertyName("encodingFormat")]
    public string? EncodingFormat { get; set; }

    /// <summary>
    /// Gets or sets the citation icon information.
    /// </summary>
    [JsonPropertyName("image")]
    public CitationImageObject? Image { get; set; }

    /// <summary>
    /// Gets or sets the keywords (max length 3, max keyword length 28).
    /// </summary>
    [JsonPropertyName("keywords")]
    public IList<string>? Keywords { get; set; }

    /// <summary>
    /// Gets or sets the sensitivity usage information for the citation.
    /// </summary>
    [JsonPropertyName("usageInfo")]
    public SensitiveUsageEntity? UsageInfo { get; set; }
}

/// <summary>
/// Represents an image object used for citation icons.
/// </summary>
public class CitationImageObject
{
    /// <summary>
    /// Gets or sets the schema.org type. Always "ImageObject".
    /// </summary>
    [JsonPropertyName("@type")]
    public string Type { get; set; } = "ImageObject";

    /// <summary>
    /// Gets or sets the icon name. See <see cref="CitationIcon"/> for known values.
    /// </summary>
    [JsonPropertyName("name")]
    public required string Name { get; set; }
}

/// <summary>
/// Known citation icon names.
/// </summary>
public static class CitationIcon
{
    /// <summary>Microsoft Word icon.</summary>
    public const string MicrosoftWord = "Microsoft Word";

    /// <summary>Microsoft Excel icon.</summary>
    public const string MicrosoftExcel = "Microsoft Excel";

    /// <summary>Microsoft PowerPoint icon.</summary>
    public const string MicrosoftPowerPoint = "Microsoft PowerPoint";

    /// <summary>Microsoft OneNote icon.</summary>
    public const string MicrosoftOneNote = "Microsoft OneNote";

    /// <summary>Microsoft SharePoint icon.</summary>
    public const string MicrosoftSharePoint = "Microsoft SharePoint";

    /// <summary>Microsoft Visio icon.</summary>
    public const string MicrosoftVisio = "Microsoft Visio";

    /// <summary>Microsoft Loop icon.</summary>
    public const string MicrosoftLoop = "Microsoft Loop";

    /// <summary>Microsoft Whiteboard icon.</summary>
    public const string MicrosoftWhiteboard = "Microsoft Whiteboard";

    /// <summary>Adobe Illustrator icon.</summary>
    public const string AdobeIllustrator = "Adobe Illustrator";

    /// <summary>Adobe Photoshop icon.</summary>
    public const string AdobePhotoshop = "Adobe Photoshop";

    /// <summary>Adobe InDesign icon.</summary>
    public const string AdobeInDesign = "Adobe InDesign";

    /// <summary>Adobe Flash icon.</summary>
    public const string AdobeFlash = "Adobe Flash";

    /// <summary>Sketch icon.</summary>
    public const string Sketch = "Sketch";

    /// <summary>Source code icon.</summary>
    public const string SourceCode = "Source Code";

    /// <summary>Image icon.</summary>
    public const string Image = "Image";

    /// <summary>GIF icon.</summary>
    public const string Gif = "GIF";

    /// <summary>Video icon.</summary>
    public const string Video = "Video";

    /// <summary>Sound icon.</summary>
    public const string Sound = "Sound";

    /// <summary>ZIP icon.</summary>
    public const string Zip = "ZIP";

    /// <summary>Text icon.</summary>
    public const string Text = "Text";

    /// <summary>PDF icon.</summary>
    public const string Pdf = "PDF";
}

/// <summary>
/// Known encoding format MIME types for citation documents.
/// </summary>
public static class EncodingFormats
{
    /// <summary>Adaptive card encoding format.</summary>
    public const string AdaptiveCard = "application/vnd.microsoft.card.adaptive";
}

/// <summary>
/// Helper class for building citation appearance documents.
/// </summary>
public class CitationAppearance
{
    /// <summary>
    /// Gets or sets the name of the document (max length 80).
    /// </summary>
    public required string Name { get; set; }

    /// <summary>
    /// Gets or sets a stringified adaptive card with additional information.
    /// </summary>
    public string? Text { get; set; }

    /// <summary>
    /// Gets or sets the URL of the document.
    /// </summary>
    public Uri? Url { get; set; }

    /// <summary>
    /// Gets or sets the extract of the referenced content (max length 160).
    /// </summary>
    public required string Abstract { get; set; }

    /// <summary>
    /// Gets or sets the encoding format of the text. See <see cref="EncodingFormats"/> for known values.
    /// </summary>
    public string? EncodingFormat { get; set; }

    /// <summary>
    /// Gets or sets the citation icon name. See <see cref="CitationIcon"/> for known values.
    /// </summary>
    public string? Icon { get; set; }

    /// <summary>
    /// Gets or sets the keywords (max length 3, max keyword length 28).
    /// </summary>
    public IList<string>? Keywords { get; set; }

    /// <summary>
    /// Gets or sets the sensitivity usage information.
    /// </summary>
    public SensitiveUsageEntity? UsageInfo { get; set; }

    /// <summary>
    /// Converts this appearance to a <see cref="CitationAppearanceDocument"/>.
    /// </summary>
    /// <returns>The appearance document.</returns>
    public CitationAppearanceDocument ToDocument()
    {
        return new()
        {
            Name = Name,
            Text = Text,
            Url = Url,
            Abstract = Abstract,
            EncodingFormat = EncodingFormat,
            Image = Icon is null ? null : new CitationImageObject() { Name = Icon },
            Keywords = Keywords,
            UsageInfo = UsageInfo
        };
    }
}
