// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq.Expressions;
using Microsoft.AspNetCore.Mvc.TestCommon;
using Xunit;

namespace Microsoft.AspNetCore.Mvc.Rendering
{
    public class HtmlHelperTextBoxTest
    {
        [Theory]
        [InlineData("text")]
        [InlineData("search")]
        [InlineData("url")]
        [InlineData("tel")]
        [InlineData("email")]
        [InlineData("number")]
        public void TextBoxFor_GeneratesPlaceholderAttribute_WhenDisplayAttributePromptIsSetAndTypeIsValid(string type)
        {
            // Arrange
            var model = new TextBoxModel();
            var helper = DefaultTemplatesUtilities.GetHtmlHelper(model);

            // Act
            var textBox = helper.TextBoxFor(m => m.Property1, new { type });

            // Assert
            var result = HtmlContentUtilities.HtmlContentToString(textBox);
            Assert.Contains(@"placeholder=""HtmlEncode[[placeholder]]""", result, StringComparison.Ordinal);
        }

        [Theory]
        [InlineData("hidden")]
        [InlineData("date")]
        [InlineData("time")]
        [InlineData("range")]
        [InlineData("color")]
        [InlineData("checkbox")]
        [InlineData("radio")]
        [InlineData("submit")]
        [InlineData("reset")]
        [InlineData("button")]
        [InlineData("image")]
        [InlineData("file")]
        public void TextBoxFor_DoesNotGeneratePlaceholderAttribute_WhenDisplayAttributePromptIsSetAndTypeIsInvalid(string type)
        {
            // Arrange
            var model = new TextBoxModel();
            var helper = DefaultTemplatesUtilities.GetHtmlHelper(model);

            // Act
            var textBox = helper.TextBoxFor(m => m.Property1, new { type });

            // Assert
            var result = HtmlContentUtilities.HtmlContentToString(textBox);
            Assert.DoesNotContain(@"placeholder=""HtmlEncode[[placeholder]]""", result, StringComparison.Ordinal);
        }

        public static TheoryData TextBoxFor_UsesModelValueForComplexExpressionsData
        {
            get
            {
                return new TheoryData<Expression<Func<ComplexModel, string>>, string>
                {
                    {
                        model => model.Property3["key"],
                        @"<input id=""HtmlEncode[[pre_Property3_key_]]"" name=""HtmlEncode[[pre.Property3[key]]]"" " +
                        @"type=""HtmlEncode[[text]]"" value=""HtmlEncode[[Prop3Val]]"" />"
                    },
                    {
                        model => model.Property4.Property5,
                        @"<input id=""HtmlEncode[[pre_Property4_Property5]]"" name=""HtmlEncode[[pre.Property4.Property5]]"" " +
                        @"type=""HtmlEncode[[text]]"" value=""HtmlEncode[[Prop5Val]]"" />"
                    },
                    {
                        model => model.Property4.Property6[0],
                        @"<input id=""HtmlEncode[[pre_Property4_Property6_0_]]"" " +
                        @"name=""HtmlEncode[[pre.Property4.Property6[0]]]"" type=""HtmlEncode[[text]]"" value=""HtmlEncode[[Prop6Val]]"" />"
                    }
                };
            }
        }

        [Theory]
        [MemberData(nameof(TextBoxFor_UsesModelValueForComplexExpressionsData))]
        public void TextBoxFor_ComplexExpressions_UsesModelValueForComplexExpressions(
            Expression<Func<ComplexModel, string>> expression,
            string expected)
        {
            // Arrange
            var model = new ComplexModel();
            var helper = DefaultTemplatesUtilities.GetHtmlHelper(model);
            helper.ViewData.TemplateInfo.HtmlFieldPrefix = "pre";

            helper.ViewData.Model.Property3["key"] = "Prop3Val";
            helper.ViewData.Model.Property4.Property5 = "Prop5Val";
            helper.ViewData.Model.Property4.Property6.Add("Prop6Val");

            // Act
            var result = helper.TextBoxFor(expression);

            // Assert
            Assert.Equal(expected, HtmlContentUtilities.HtmlContentToString(result));
        }

        public static TheoryData TextBoxFor_UsesModelStateValueForComplexExpressionsData
        {
            get
            {
                return new TheoryData<Expression<Func<ComplexModel, string>>, string>
                {
                    {
                        model => model.Property3["key"],
                        @"<input id=""HtmlEncode[[pre_Property3_key_]]"" name=""HtmlEncode[[pre.Property3[key]]]"" " +
                        @"type=""HtmlEncode[[text]]"" value=""HtmlEncode[[MProp3Val]]"" />"
                    },
                    {
                        model => model.Property4.Property5,
                        @"<input id=""HtmlEncode[[pre_Property4_Property5]]"" name=""HtmlEncode[[pre.Property4.Property5]]"" " +
                        @"type=""HtmlEncode[[text]]"" value=""HtmlEncode[[MProp5Val]]"" />"
                    },
                    {
                        model => model.Property4.Property6[0],
                        @"<input id=""HtmlEncode[[pre_Property4_Property6_0_]]"" " +
                        @"name=""HtmlEncode[[pre.Property4.Property6[0]]]"" type=""HtmlEncode[[text]]"" value=""HtmlEncode[[MProp6Val]]"" />"
                    }
                };
            }
        }

        [Theory]
        [MemberData(nameof(TextBoxFor_UsesModelStateValueForComplexExpressionsData))]
        public void TextBoxFor_ComplexExpressions_UsesModelStateValueForComplexExpressions(
            Expression<Func<ComplexModel, string>> expression,
            string expected)
        {
            // Arrange
            var model = new ComplexModel();
            var helper = DefaultTemplatesUtilities.GetHtmlHelper(model);
            helper.ViewData.TemplateInfo.HtmlFieldPrefix = "pre";

            helper.ViewData.ModelState.SetModelValue("pre.Property3[key]", "MProp3Val", "MProp3Val");
            helper.ViewData.ModelState.SetModelValue("pre.Property4.Property5", "MProp5Val", "MProp5Val");
            helper.ViewData.ModelState.SetModelValue("pre.Property4.Property6[0]", "MProp6Val", "MProp6Val");

            helper.ViewData.Model.Property3["key"] = "Prop3Val";
            helper.ViewData.Model.Property4.Property5 = "Prop5Val";
            helper.ViewData.Model.Property4.Property6.Add("Prop6Val");

            // Act
            var result = helper.TextBoxFor(expression);

            // Assert
            Assert.Equal(expected, HtmlContentUtilities.HtmlContentToString(result));
        }

        public class ComplexModel
        {
            public string Property1 { get; set; }

            public byte[] Bytes { get; set; }

            [Required]
            public string Property2 { get; set; }

            public Dictionary<string, string> Property3 { get; } = new Dictionary<string, string>();

            public NestedClass Property4 { get; } = new NestedClass();
        }

        public class NestedClass
        {
            public string Property5 { get; set; }

            public List<string> Property6 { get; } = new List<string>();
        }

        private class TextBoxModel
        {
            [Display(Prompt = "placeholder")]
            public string Property1 { get; set; }
        }
    }
}