// 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.Collections.Generic;
using System.Reflection;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Xunit;

namespace Microsoft.AspNetCore.Mvc.ApplicationModels
{
    public class ParameterModelTest
    {
        [Fact]
        public void CopyConstructor_CopiesAllProperties()
        {
            // Arrange
            var parameter = new ParameterModel(typeof(TestController).GetMethod("Edit").GetParameters()[0],
                                               new List<object>() { new FromBodyAttribute() });

            parameter.Action = new ActionModel(typeof(TestController).GetMethod("Edit"), new List<object>());
            parameter.BindingInfo = new BindingInfo()
            {
                BindingSource = BindingSource.Body
            };

            parameter.ParameterName = "id";
            parameter.Properties.Add(new KeyValuePair<object, object>("test key", "test value"));

            // Act
            var parameter2 = new ParameterModel(parameter);

            // Assert
            foreach (var property in typeof(ParameterModel).GetProperties())
            {
                if (property.Name.Equals("BindingInfo"))
                {
                    // This test excludes other mutable objects on purpose because we deep copy them.
                    continue;
                }

                var value1 = property.GetValue(parameter);
                var value2 = property.GetValue(parameter2);

                if (typeof(IEnumerable<object>).IsAssignableFrom(property.PropertyType))
                {
                    Assert.Equal<object>((IEnumerable<object>)value1, (IEnumerable<object>)value2);

                    // Ensure non-default value
                    Assert.NotEmpty((IEnumerable<object>)value1);
                }
                else if (typeof(IDictionary<object, object>).IsAssignableFrom(property.PropertyType))
                {
                    Assert.Equal(value1, value2);

                    // Ensure non-default value
                    Assert.NotEmpty((IDictionary<object, object>)value1);
                }
                else if (property.PropertyType.GetTypeInfo().IsValueType ||
                    Nullable.GetUnderlyingType(property.PropertyType) != null)
                {
                    Assert.Equal(value1, value2);

                    // Ensure non-default value
                    Assert.NotEqual(value1, Activator.CreateInstance(property.PropertyType));
                }
                else if (property.Name.Equals(nameof(ActionModel.DisplayName)))
                {
                    // DisplayName is re-calculated, hence reference equality wouldn't work.
                    Assert.Equal(value1, value2);
                }
                else
                {
                    Assert.Same(value1, value2);

                    // Ensure non-default value
                    Assert.NotNull(value1);
                }
            }
        }

        private class TestController
        {
            public void Edit(int id)
            {
            }
        }
    }
}