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

using System.Linq;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Logging.Abstractions;
using Xunit;

namespace Microsoft.AspNetCore.Mvc.ApplicationModels
{
    public class PageRouteModelFactoryTest
    {
        [Fact]
        public void CreateRouteModel_AddsSelector()
        {
            // Arrange
            var relativePath = "/Pages/Users/Profile.cshtml";
            var options = new RazorPagesOptions();
            var routeModelFactory = new PageRouteModelFactory(options, NullLogger.Instance);

            // Act
            var routeModel = routeModelFactory.CreateRouteModel(relativePath, "{id?}");

            // Assert
            Assert.Equal(relativePath, routeModel.RelativePath);
            Assert.Equal("/Users/Profile", routeModel.ViewEnginePath);
            Assert.Null(routeModel.AreaName);

            Assert.Collection(
                routeModel.Selectors,
                selector => Assert.Equal("Users/Profile/{id?}", selector.AttributeRouteModel.Template));

            Assert.Collection(
                routeModel.RouteValues,
                kvp =>
                {
                    Assert.Equal("page", kvp.Key);
                    Assert.Equal("/Users/Profile", kvp.Value);
                });
        }

        [Fact]
        public void CreateRouteModel_AddsMultipleSelectorsForIndexPage()
        {
            // Arrange
            var relativePath = "/Pages/Users/Profile/Index.cshtml";
            var options = new RazorPagesOptions();
            var routeModelFactory = new PageRouteModelFactory(options, NullLogger.Instance);

            // Act
            var routeModel = routeModelFactory.CreateRouteModel(relativePath, "{id?}");

            // Assert
            Assert.Equal(relativePath, routeModel.RelativePath);
            Assert.Equal("/Users/Profile/Index", routeModel.ViewEnginePath);

            Assert.Collection(
                routeModel.Selectors,
                selector => Assert.Equal("Users/Profile/Index/{id?}", selector.AttributeRouteModel.Template),
                selector => Assert.Equal("Users/Profile/{id?}", selector.AttributeRouteModel.Template));

            Assert.Collection(
                routeModel.RouteValues,
                kvp =>
                {
                    Assert.Equal("page", kvp.Key);
                    Assert.Equal("/Users/Profile/Index", kvp.Value);
                });
        }

        [Fact]
        public void CreateRouteModel_DoesNotAddMultipleSelectorsWhenRouteTemplateIsAbsolute()
        {
            // Arrange
            var relativePath = "/Pages/Users/Profile/Index.cshtml";
            var options = new RazorPagesOptions();
            var routeModelFactory = new PageRouteModelFactory(options, NullLogger.Instance);

            // Act
            var routeModel = routeModelFactory.CreateRouteModel(relativePath, "/my-override");

            // Assert
            Assert.Equal(relativePath, routeModel.RelativePath);
            Assert.Equal("/Users/Profile/Index", routeModel.ViewEnginePath);

            Assert.Collection(
                routeModel.Selectors,
                selector => Assert.Equal("my-override", selector.AttributeRouteModel.Template));

            Assert.Collection(
                routeModel.RouteValues,
                kvp =>
                {
                    Assert.Equal("page", kvp.Key);
                    Assert.Equal("/Users/Profile/Index", kvp.Value);
                });
        }

        [Fact]
        public void CreateAreaRouteModel_AddsSelector()
        {
            // Arrange
            var relativePath = "/Areas/TestArea/Pages/Users/Profile.cshtml";
            var options = new RazorPagesOptions();
            var routeModelFactory = new PageRouteModelFactory(options, NullLogger.Instance);

            // Act
            var routeModel = routeModelFactory.CreateAreaRouteModel(relativePath, "{id?}");

            // Assert
            Assert.Equal(relativePath, routeModel.RelativePath);
            Assert.Equal("/Users/Profile", routeModel.ViewEnginePath);
            Assert.Equal("TestArea", routeModel.AreaName);

            Assert.Collection(
                routeModel.Selectors,
                selector => Assert.Equal("TestArea/Users/Profile/{id?}", selector.AttributeRouteModel.Template));

            Assert.Collection(
                routeModel.RouteValues.OrderBy(kvp => kvp.Key),
                kvp =>
                {
                    Assert.Equal("area", kvp.Key);
                    Assert.Equal("TestArea", kvp.Value);
                },
                kvp =>
                {
                    Assert.Equal("page", kvp.Key);
                    Assert.Equal("/Users/Profile", kvp.Value);
                });
        }

        [Fact]
        public void CreateAreaRouteModel_AddsMultipleSelectorsForIndexPage()
        {
            // Arrange
            var relativePath = "/Areas/TestArea/Pages/Users/Profile/Index.cshtml";
            var options = new RazorPagesOptions();
            var routeModelFactory = new PageRouteModelFactory(options, NullLogger.Instance);

            // Act
            var routeModel = routeModelFactory.CreateAreaRouteModel(relativePath, "{id?}");

            // Assert
            Assert.Equal(relativePath, routeModel.RelativePath);
            Assert.Equal("/Users/Profile/Index", routeModel.ViewEnginePath);
            Assert.Equal("TestArea", routeModel.AreaName);

            Assert.Collection(
                routeModel.Selectors,
                selector => Assert.Equal("TestArea/Users/Profile/Index/{id?}", selector.AttributeRouteModel.Template),
                selector => Assert.Equal("TestArea/Users/Profile/{id?}", selector.AttributeRouteModel.Template));

            Assert.Collection(
                routeModel.RouteValues.OrderBy(kvp => kvp.Key),
                kvp =>
                {
                    Assert.Equal("area", kvp.Key);
                    Assert.Equal("TestArea", kvp.Value);
                },
                kvp =>
                {
                    Assert.Equal("page", kvp.Key);
                    Assert.Equal("/Users/Profile/Index", kvp.Value);
                });
        }

        [Theory]
        [InlineData("/Areas/About.cshtml")]
        [InlineData("/Areas/MyArea/Index.cshtml")]
        public void TryParseAreaPath_ReturnsFalse_IfPathDoesNotConform(string path)
        {
            // Arrange
            var options = new RazorPagesOptions();
            var routeModelFactory = new PageRouteModelFactory(options, NullLogger.Instance);

            // Act
            var success = routeModelFactory.TryParseAreaPath(path, out _);

            // Assert
            Assert.False(success);
        }

        [Theory]
        [InlineData("/Areas/MyArea/Views/About.cshtml")]
        [InlineData("/Areas/MyArea/SubDir/Pages/Index.cshtml")]
        [InlineData("/Areas/MyArea/NotPages/SubDir/About.cshtml")]
        public void TryParseAreaPath_ReturnsFalse_IfPathDoesNotBelongToRootDirectory(string path)
        {
            // Arrange
            var options = new RazorPagesOptions();
            var routeModelFactory = new PageRouteModelFactory(options, NullLogger.Instance);

            // Act
            var success = routeModelFactory.TryParseAreaPath(path, out _);

            // Assert
            Assert.False(success);
        }

        [Theory]
        [InlineData("/Areas/MyArea/Pages/Index.cshtml", "MyArea", "/Index")]
        [InlineData("/Areas/Accounts/Pages/Manage/Edit.cshtml", "Accounts", "/Manage/Edit")]
        public void TryParseAreaPath_ParsesAreaPath(
            string path,
            string expectedArea,
            string expectedViewEnginePath)
        {
            // Arrange
            var options = new RazorPagesOptions();
            var routeModelFactory = new PageRouteModelFactory(options, NullLogger.Instance);

            // Act
            var success = routeModelFactory.TryParseAreaPath(path, out var result);

            // Assert
            Assert.True(success);
            Assert.Equal(expectedArea, result.areaName);
            Assert.Equal(expectedViewEnginePath, result.viewEnginePath);
        }
    }
}
