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

using System;
using System.Globalization;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Testing;
using Xunit;

namespace Microsoft.AspNetCore.Mvc.ViewFeatures
{
    /// <summary>
    /// Test the <see cref="IHtmlHelper.Value" /> and <see cref="IHtmlHelper{TModel}.ValueFor"/> methods.
    /// </summary>
    public class HtmlHelperValueTest
    {
        // Value

        [Fact]
        public void ValueNotInTemplate_GetsValueFromViewData()
        {
            // Arrange
            var helper = GetHtmlHelper();

            // Act
            var html = helper.Value("StringProperty", format: null);

            // Assert
            Assert.Equal("ViewDataValue", html);
        }

        [Fact]
        public void ValueInTemplate_GetsValueFromPrefixedViewDataEntry()
        {
            // Arrange
            var helper = GetHtmlHelper();
            helper.ViewData["Prefix.StringProperty"] = "PrefixedViewDataValue";
            helper.ViewData.TemplateInfo.HtmlFieldPrefix = "Prefix";

            // Act
            var html = helper.Value("StringProperty", format: null);

            // Assert
            Assert.Equal("PrefixedViewDataValue", html);
        }

        [Fact]
        public void ValueNotInTemplate_GetsValueFromPropertyOfViewDataEntry()
        {
            // Arrange
            var helper = GetHtmlHelper();
            helper.ViewData["Prefix"] = new { StringProperty = "ContainedViewDataValue" };

            // Act
            var html = helper.Value("Prefix.StringProperty", format: null);

            // Assert
            Assert.Equal("ContainedViewDataValue", html);
        }

        [Fact]
        public void ValueInTemplate_GetsValueFromPropertyOfViewDataEntry()
        {
            // Arrange
            var helper = GetHtmlHelper();
            helper.ViewData["Prefix"] = new { StringProperty = "ContainedViewDataValue" };
            helper.ViewData.TemplateInfo.HtmlFieldPrefix = "Prefix";

            // Act
            var html = helper.Value("StringProperty", format: null);

            // Assert
            Assert.Equal("ContainedViewDataValue", html);
        }

        [Fact]
        public void ValueNotInTemplate_GetsValueFromModel_IfNoViewDataEntry()
        {
            // Arrange
            var helper = GetHtmlHelper();
            helper.ViewData.Clear();

            // Act
            var html = helper.Value("StringProperty", format: null);

            // Assert
            Assert.Equal("ModelStringPropertyValue", html);
        }

        [Fact]
        public void ValueInTemplate_GetsValueFromModel_IfNoViewDataEntry()
        {
            // Arrange
            var helper = GetHtmlHelper();
            helper.ViewData.Clear();
            helper.ViewData.TemplateInfo.HtmlFieldPrefix = "Prefix";

            // Act
            var html = helper.Value("StringProperty", format: null);

            // Assert
            Assert.Equal("ModelStringPropertyValue", html);
        }

        [Fact]
        public void ValueNotInTemplate_GetsValueFromViewData_EvenIfNull()
        {
            // Arrange
            var helper = GetHtmlHelper();
            helper.ViewData["StringProperty"] = null;

            // Act
            var html = helper.Value("StringProperty", format: null);

            // Assert
            Assert.Empty(html);
        }

        [Fact]
        public void ValueInTemplate_GetsValueFromViewData_EvenIfNull()
        {
            // Arrange
            var helper = GetHtmlHelper();
            helper.ViewData["Prefix.StringProperty"] = null;
            helper.ViewData.TemplateInfo.HtmlFieldPrefix = "Prefix";

            // Act
            var html = helper.Value("StringProperty", format: null);

            // Assert
            Assert.Empty(html);
        }

        // ValueFor

        [Fact]
        public void ValueForGetsExpressionValueFromViewDataModel()
        {
            // Arrange
            var helper = GetHtmlHelper();

            // Act
            var html = helper.ValueFor(m => m.StringProperty, format: null);

            // Assert
            Assert.Equal("ModelStringPropertyValue", html);
        }

        // All Value Helpers including ValueForModel

        [Fact]
        public void ValueHelpersWithErrorsGetValueFromModelState()
        {
            // Arrange
            var model = new TestModel()
            {
                StringProperty = "ModelStringPropertyValue",
                ObjectProperty = "ModelObjectPropertyValue",
            };
            var helper = DefaultTemplatesUtilities.GetHtmlHelper<TestModel>(model);
            var viewData = helper.ViewData;
            viewData["StringProperty"] = "ViewDataValue";
            viewData.TemplateInfo.HtmlFieldPrefix = "FieldPrefix";

            viewData.ModelState.SetModelValue(
                "FieldPrefix.StringProperty",
                "StringPropertyRawValue",
                "StringPropertyAttemptedValue");

            viewData.ModelState.SetModelValue(
                "FieldPrefix",
                "ModelRawValue",
                "ModelAttemptedValue");

            // Act & Assert
            Assert.Equal("StringPropertyRawValue", helper.Value("StringProperty", format: null));
            Assert.Equal("StringPropertyRawValue", helper.ValueFor(m => m.StringProperty, format: null));
            Assert.Equal("ModelRawValue", helper.ValueForModel(format: null));
        }

        [Fact]
        [ReplaceCulture]
        public void ValueHelpersWithEmptyNameConvertModelValueUsingCurrentCulture()
        {
            // Arrange
            var helper = GetHtmlHelper();
            var expectedModelValue =
                "{ StringProperty = ModelStringPropertyValue, ObjectProperty = 01/01/1900 00:00:00 }";

            // Act & Assert
            Assert.Equal(expectedModelValue, helper.Value(expression: string.Empty, format: null));
            Assert.Equal(expectedModelValue, helper.Value(expression: null, format: null)); // null is another alias for current model
            Assert.Equal(expectedModelValue, helper.ValueFor(m => m, format: null));
            Assert.Equal(expectedModelValue, helper.ValueForModel(format: null));
        }

        [Fact]
        [ReplaceCulture]
        public void ValueHelpersFormatValue()
        {
            // Arrange
            var helper = GetHtmlHelper();
            var expectedModelValue =
                "-{ StringProperty = ModelStringPropertyValue, ObjectProperty = 01/01/1900 00:00:00 }-";
            var expectedObjectPropertyValue = "-01/01/1900 00:00:00-";

            // Act & Assert
            Assert.Equal(expectedModelValue, helper.ValueForModel("-{0}-"));
            Assert.Equal(expectedObjectPropertyValue, helper.Value("ObjectProperty", "-{0}-"));
            Assert.Equal(expectedObjectPropertyValue, helper.ValueFor(m => m.ObjectProperty, "-{0}-"));
        }

        [Fact]
        public void ValueHelpersDoNotEncodeValue()
        {
            // Arrange
            var model = new TestModel { StringProperty = "ModelStringPropertyValue <\"\">" };
            var helper = DefaultTemplatesUtilities.GetHtmlHelper<TestModel>(model);
            var viewData = helper.ViewData;
            viewData["StringProperty"] = "ViewDataValue <\"\">";

            viewData.ModelState.SetModelValue(
                "ObjectProperty",
                "ObjectPropertyRawValue <\"\">",
                "ObjectPropertyAttemptedValue <\"\">");

            // Act & Assert
            Assert.Equal(
                "<{ StringProperty = ModelStringPropertyValue <\"\">, ObjectProperty = (null) }>",
                helper.ValueForModel("<{0}>"));
            Assert.Equal("<ViewDataValue <\"\">>", helper.Value("StringProperty", "<{0}>"));
            Assert.Equal("<ModelStringPropertyValue <\"\">>", helper.ValueFor(m => m.StringProperty, "<{0}>"));
            Assert.Equal("ObjectPropertyRawValue <\"\">", helper.ValueFor(m => m.ObjectProperty, format: null));
        }

        private sealed class TestModel
        {
            public string StringProperty { get; set; }
            public object ObjectProperty { get; set; }

            public override string ToString()
            {
                return string.Format(
                    CultureInfo.InvariantCulture,
                    "{{ StringProperty = {0}, ObjectProperty = {1} }}",
                    StringProperty ?? "(null)",
                    ObjectProperty ?? "(null)");
            }
        }

        private static HtmlHelper<TestModel> GetHtmlHelper()
        {
            var model = new TestModel
            {
                StringProperty = "ModelStringPropertyValue",
                ObjectProperty = new DateTime(1900, 1, 1, 0, 0, 0),
            };
            var helper = DefaultTemplatesUtilities.GetHtmlHelper<TestModel>(model);
            helper.ViewData["StringProperty"] = "ViewDataValue";

            return helper;
        }
    }
}
