﻿// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

#nullable disable

using Microsoft.CodeAnalysis.CSharp.DocumentationComments;
using Microsoft.CodeAnalysis.Test.Utilities;
using Microsoft.CodeAnalysis.VisualBasic.DocumentationComments;
using Roslyn.Test.Utilities;
using Xunit;

namespace Microsoft.CodeAnalysis.Editor.UnitTests.DocCommentFormatting
{
    [Trait(Traits.Feature, Traits.Features.DocCommentFormatting)]
    public class DocCommentFormattingTests
    {
        private readonly CSharpDocumentationCommentFormattingService _csharpService = new CSharpDocumentationCommentFormattingService();
        private readonly VisualBasicDocumentationCommentFormattingService _vbService = new VisualBasicDocumentationCommentFormattingService();

        private void TestFormat(string xmlFragment, string expectedCSharp, string expectedVB)
        {
            var csharpFormattedText = _csharpService.Format(xmlFragment);
            var vbFormattedText = _vbService.Format(xmlFragment);

            Assert.Equal(expectedCSharp, csharpFormattedText);
            Assert.Equal(expectedVB, vbFormattedText);
        }

        private void TestFormat(string xmlFragment, string expected)
            => TestFormat(xmlFragment, expected, expected);

        [Fact]
        public void CTag()
        {
            var comment = "Class <c>Point</c> models a point in a two-dimensional plane.";
            var expected = "Class Point models a point in a two-dimensional plane.";

            TestFormat(comment, expected);
        }

        [Fact]
        public void ExampleAndCodeTags()
        {
            var comment = @"This method changes the point's location by the given x- and y-offsets.
            <example>For example:
            <code>
            Point p = new Point(3,5);
            p.Translate(-1,3);
            </code>
            results in <c>p</c>'s having the value (2,8).
            </example>";

            var expected = "This method changes the point's location by the given x- and y-offsets. For example:\r\n\r\n            Point p = new Point(3,5);\r\n            p.Translate(-1,3);\r\n            \r\n\r\nresults in p's having the value (2,8).";

            TestFormat(comment, expected);
        }

        [Fact]
        public void ListTag()
        {
            var comment = @"Here is an example of a bulleted list:
        <list type=""bullet"">
        <item>
        <description>Item 1.</description>
        </item>
        <item>
        <description>Item 2.</description>
        </item>
        </list>";

            var expected = "Here is an example of a bulleted list:\r\n\r\n• Item 1.\r\n• Item 2.";

            TestFormat(comment, expected);
        }

        [Fact]
        public void ParaTag()
        {
            var comment = @"This is the entry point of the Point class testing program.
        <para>This program tests each method and operator, and
        is intended to be run after any non-trivial maintenance has
        been performed on the Point class.</para>";

            var expected =
@"This is the entry point of the Point class testing program.

This program tests each method and operator, and is intended to be run after any non-trivial maintenance has been performed on the Point class.";

            TestFormat(comment, expected);
        }

        [Fact]
        public void TestPermissionTag()
        {
            var comment = @"<permission cref=""System.Security.PermissionSet"">Everyone can access this method.</permission>";

            var expected = @"Everyone can access this method.";

            TestFormat(comment, expected);
        }

        [Fact]
        public void SeeTag()
        {
            var comment = @"<see cref=""AnotherFunction""/>";

            var expected = @"AnotherFunction";

            TestFormat(comment, expected);
        }

        [Fact]
        public void SeeAlsoTag()
        {
            var comment = @"<seealso cref=""AnotherFunction""/>";

            var expected = @"AnotherFunction";

            TestFormat(comment, expected);
        }

        [Fact]
        public void ValueTag()
        {
            var comment = @"<value>Property <c>X</c> represents the point's x-coordinate.</value>";

            var expected = @"Property X represents the point's x-coordinate.";

            TestFormat(comment, expected);
        }

        [Fact]
        public void TestParamRefTag()
        {
            var comment =
@"This constructor initializes the new Point to 
(<paramref name=""xor""/>,<paramref name=""yor""/>).";

            var expected = "This constructor initializes the new Point to (xor,yor).";

            TestFormat(comment, expected);
        }

        [Fact]
        public void TestTypeParamRefTag()
        {
            var comment = @"This method fetches data and returns a list of  <typeparamref name=""Z""/>.";

            var expected = @"This method fetches data and returns a list of Z.";

            TestFormat(comment, expected);
        }

        [Fact]
        public void Whitespace1()
        {
            var comment = "  This has extra whitespace.  ";

            var expected = "This has extra whitespace.";

            TestFormat(comment, expected);
        }

        [Fact]
        public void Whitespace2()
        {
            var comment =
@"
This has extra
whitespace.
";

            var expected = "This has extra whitespace.";

            TestFormat(comment, expected);
        }

        [Fact]
        public void Whitespace3()
        {
            var comment = "This  has  extra  whitespace.";
            var expected = "This has extra whitespace.";

            TestFormat(comment, expected);
        }

        [Fact]
        public void Paragraphs1()
        {
            var comment =
@"
<para>This is part of a paragraph.</para>
";
            var expected = "This is part of a paragraph.";

            TestFormat(comment, expected);
        }

        [Fact]
        public void Paragraphs2()
        {
            var comment =
@"
<para>This is part of a paragraph.</para>
<para>This is also part of a paragraph.</para>
";

            var expected =
@"This is part of a paragraph.

This is also part of a paragraph.";

            TestFormat(comment, expected);
        }

        [Fact]
        public void Paragraphs3()
        {
            var comment =
@"
This is a summary.
<para>This is part of a paragraph.</para>
";

            var expected =
@"This is a summary.

This is part of a paragraph.";

            TestFormat(comment, expected);
        }

        [Fact]
        public void Paragraphs4()
        {
            var comment =
@"
<para>This is part of a paragraph.</para> This is part of the summary, too.
";

            var expected =
@"This is part of a paragraph.

This is part of the summary, too.";

            TestFormat(comment, expected);
        }

        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/32838")]
        public void Paragraphs5()
        {
            var comment =
@"
<para>This is part of a<br/>paragraph.</para>
<para>This is also part of a paragraph.</para>
";

            var expected =
@"This is part of a
paragraph.

This is also part of a paragraph.";

            TestFormat(comment, expected);
        }

        [Theory]
        [InlineData("<br/><br/>")]
        [InlineData("<br/><br/><br/>")]
        [WorkItem("https://github.com/dotnet/roslyn/issues/32838")]
        public void Paragraphs6(string lineBreak)
        {
            var comment =
$@"
<para>This is part of a{lineBreak}paragraph.</para>
<para>This is also part of a paragraph.</para>
";

            var expected =
@"This is part of a

paragraph.

This is also part of a paragraph.";

            TestFormat(comment, expected);
        }

        [Fact]
        public void See1()
        {
            var comment = @"See <see cref=""T:System.Object"" />";

            var expected = "See System.Object";

            TestFormat(comment, expected);
        }

        [Fact]
        public void See2()
        {
            var comment = @"See <see />";

            var expected = @"See";

            TestFormat(comment, expected);
        }

        [Fact]
        public void See3()
        {
            var comment = @"See <see langword=""true"" />";

            var expected = "See true";

            TestFormat(comment, expected);
        }

        [Fact]
        public void See4()
        {
            var comment = @"See <see href=""https://github.com"" />";

            var expected = "See https://github.com";

            TestFormat(comment, expected);
        }

        [Fact]
        public void See5()
        {
            var comment = @"See <see href=""https://github.com"">GitHub</see>";

            var expected = "See GitHub";

            TestFormat(comment, expected);
        }

        [Fact]
        public void See6()
        {
            var comment = @"See <see href=""https://github.com""></see>";

            var expected = "See https://github.com";

            TestFormat(comment, expected);
        }

        [Fact]
        public void SeeAlso1()
        {
            var comment = @"See also <seealso cref=""T:System.Object"" />";

            var expected = @"See also System.Object";

            TestFormat(comment, expected);
        }

        [Fact]
        public void SeeAlso2()
        {
            var comment = @"See also <seealso />";

            var expected = @"See also";

            TestFormat(comment, expected);
        }

        [Fact]
        public void SeeAlso3()
        {
            var comment = @"See also <seealso langword=""true"" />";

            var expected = "See also true";

            TestFormat(comment, expected);
        }

        [Fact]
        public void SeeAlso4()
        {
            var comment = @"See also <seealso href=""https://github.com"" />";

            var expected = "See also https://github.com";

            TestFormat(comment, expected);
        }

        [Fact]
        public void SeeAlso5()
        {
            var comment = @"See also <seealso href=""https://github.com"">GitHub</seealso>";

            var expected = "See also GitHub";

            TestFormat(comment, expected);
        }

        [Fact]
        public void SeeAlso6()
        {
            var comment = @"See also <seealso href=""https://github.com""></seealso>";

            var expected = "See also https://github.com";

            TestFormat(comment, expected);
        }
    }
}
