// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using Microsoft.AspNetCore.Testing;
using Xunit;
using Xunit.Sdk;

namespace Microsoft.AspNetCore.Mvc.Formatters.Xml
{
    public class XmlAssertTest
    {
        [Theory]
        [InlineData("<A>hello</A>", "<A>hey</A>")]
        [InlineData("<A><B>hello world</B></A>", "<A><B>hello world!!</B></A>")]
        [InlineData("<a><b>hello</b><b>world</b></a>", "<a><b>hello</b><c>world</c></a>")]
        [InlineData("<a><b>hello</b><b>world</b></a>", "<a><b>hello</b><b attribute=\"value\">world</b></a>")]
        [InlineData("<a>hello<b>world</b>hello</a>", "<a>hello<b>world</b>goodbye</a>")]
        [InlineData("<a><b>hello</b><b>world</b></a>", "<a><b>hello</b><b>goodbye</b></a>")]
        public void Throws_WithMismatchedTextNodes(string input1, string input2)
        {
            var equalException = Assert.Throws<EqualException>(() => XmlAssert.Equal(input1, input2));
            Assert.Equal(input1, equalException.Expected);
            Assert.Equal(input2, equalException.Actual);
        }

        [Theory]
        [InlineData("<a>hello<b>world</b>hello</a>", "<a>hello<b>world</b>hello</a>")]
        [InlineData("<a>hello<b/>hello</a>", "<a>hello<b/>hello</a>")]
        [InlineData(
            "<a>hello<b color=\"red\" siz=\"medium\">world</b>hello</a>",
            "<a>hello<b siz=\"medium\" color=\"red\">world</b>hello</a>")]
        public void ReturnsSuccessfully_WithMatchingTextNodes(string input1, string input2)
        {
            XmlAssert.Equal(input1, input2);
        }

        public static TheoryData<string, string> EmptyElementData
        {
            get
            {
                var data = new TheoryData<string, string>
                {
                    { "<A></A>", "<A></A>" },
                    { "<A><!-- comment1 --><B></B></A>", "<A><!-- comment1 --><B></B></A>" },
                    { "<A/>", "<A/>" },
                };

                // DeepEquals returns false even though the generated XML documents are equal.
                // This is fixed in Mono 4.3.0
                if (!TestPlatformHelper.IsMono)
                {
                    data.Add("<A><![CDATA[<greeting></greeting>]]></A>", "<A><![CDATA[<greeting></greeting>]]></A>");
                }

                return data;
            }
        }

        [Theory]
        [MemberData(nameof(EmptyElementData))]
        public void ReturnsSuccessfully_WithEmptyElements(string input1, string input2)
        {
            XmlAssert.Equal(input1, input2);
        }

        [Theory]
        [InlineData("<?xml version=\"1.0\" encoding=\"UTF-8\"?><A></A>",
            "<A></A>")]
        [InlineData("<?xml version=\"1.0\" encoding=\"UTF-8\"?><A></A>",
            "<?xml version=\"1.0\" encoding=\"UTF-16\"?><A></A>")]
        public void Throws_WithMismatchedXmlDeclaration(string input1, string input2)
        {
            Assert.Throws<EqualException>(() => XmlAssert.Equal(input1, input2));
        }

        [ConditionalFact]
        // DeepEquals returns false even though the generated XML documents are equal.
        // This is fixed in Mono 4.3.0
        [FrameworkSkipCondition(RuntimeFrameworks.Mono)]
        public void ReturnsSuccessfully_WithMatchingXmlDeclaration_IgnoringCase()
        {
            // Arrange
            var input1 = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
                        "<A><B color=\"red\" size=\"medium\">hello world</B></A>";
            var input2 = "<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
                        "<A><B size=\"medium\" color=\"red\">hello world</B></A>";

            // Act and Assert
            XmlAssert.Equal(input1, input2);
        }

        [Theory]
        [InlineData(
            "<A color=\"red\" size=\"medium\" />",
            "<A size=\"medium\" color=\"red\" />")]
        [InlineData(
            "<A><B color=\"red\" size=\"medium\">hello world</B></A>",
            "<A><B size=\"medium\" color=\"red\">hello world</B></A>")]
        public void ReturnsSuccessfully_WithMatchingContent_IgnoringAttributeOrder(string input1, string input2)
        {
            XmlAssert.Equal(input1, input2);
        }

        [Fact]
        public void Throws_WithMismatchedAttributeValues_ReorderingAttributes()
        {
            // Arrange
            var expected = "<A>hello<B color=\"red\" size=\"medium\">hello world</B>hi</A>";
            var actual = "<A>hello<B size=\"Medium\" color=\"red\">hello world</B>hi</A>";
            var exceptionMessageForExpected = "<A>hello<B color=\"red\" size=\"medium\">hello world</B>hi</A>";
            var exceptionMessageForActual = "<A>hello<B color=\"red\" size=\"Medium\">hello world</B>hi</A>";

            // Act and Assert
            var equalException = Assert.Throws<EqualException>(() => XmlAssert.Equal(expected, actual));
            Assert.Equal(exceptionMessageForExpected, equalException.Expected);
            Assert.Equal(exceptionMessageForActual, equalException.Actual);
        }
    }
}