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

using Microsoft.CodeAnalysis.Testing;
using VerifyCS = Microsoft.AspNetCore.Analyzers.Verifiers.CSharpAnalyzerVerifier<Microsoft.AspNetCore.Analyzers.RouteHandlers.RouteHandlerAnalyzer>;

namespace Microsoft.AspNetCore.Analyzers.RouteHandlers;

public partial class DetectAmbiguousMappedRoutesTest
{
    [Fact]
    public async Task DuplicateRoutes_SameHttpMethod_HasDiagnostics()
    {
        // Arrange
        var source = @"
using Microsoft.AspNetCore.Builder;
var app = WebApplication.Create();
app.MapGet({|#0:""/""|}, () => Hello());
app.MapGet({|#1:""/""|}, () => Hello());
void Hello() { }
";

        var expectedDiagnostics = new[] {
            new DiagnosticResult(DiagnosticDescriptors.AmbiguousRouteHandlerRoute).WithArguments("/").WithLocation(0),
            new DiagnosticResult(DiagnosticDescriptors.AmbiguousRouteHandlerRoute).WithArguments("/").WithLocation(1)
        };

        // Act & Assert
        await VerifyCS.VerifyAnalyzerAsync(source, expectedDiagnostics);
    }

    [Fact]
    public async Task DuplicateRoutes_SameHttpMethod_HasRequestDelegate_HasDiagnostics()
    {
        // Arrange
        var source = @"
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
var app = WebApplication.Create();
app.MapGet({|#0:""/""|}, () => Hello());
app.MapGet({|#1:""/""|}, (HttpContext context) => Task.CompletedTask);
void Hello() { }
";

        var expectedDiagnostics = new[] {
            new DiagnosticResult(DiagnosticDescriptors.AmbiguousRouteHandlerRoute).WithArguments("/").WithLocation(0),
            new DiagnosticResult(DiagnosticDescriptors.AmbiguousRouteHandlerRoute).WithArguments("/").WithLocation(1)
        };

        // Act & Assert
        await VerifyCS.VerifyAnalyzerAsync(source, expectedDiagnostics);
    }

    [Fact]
    public async Task DuplicateRoutes_SameHttpMethod_InMethod_HasDiagnostics()
    {
        // Arrange
        var source = @"
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Routing;
var app = WebApplication.Create();
void RegisterEndpoints(IEndpointRouteBuilder builder)
{
    builder.MapGet({|#0:""/""|}, () => Hello());
    builder.MapGet({|#1:""/""|}, () => Hello());
}

RegisterEndpoints(app);

void Hello() { }
";

        var expectedDiagnostics = new[] {
            new DiagnosticResult(DiagnosticDescriptors.AmbiguousRouteHandlerRoute).WithArguments("/").WithLocation(0),
            new DiagnosticResult(DiagnosticDescriptors.AmbiguousRouteHandlerRoute).WithArguments("/").WithLocation(1)
        };

        // Act & Assert
        await VerifyCS.VerifyAnalyzerAsync(source, expectedDiagnostics);
    }

    [Fact]
    public async Task DuplicateRoutes_TernaryStatement_NoDiagnostics()
    {
        // Arrange
        var source = @"
using Microsoft.AspNetCore.Builder;
var app = WebApplication.Create();
_ = (true)
    ? app.MapGet(""/"", () => Hello())
    : app.MapGet(""/"", () => Hello());
void Hello() { }
";

        // Act & Assert
        await VerifyCS.VerifyAnalyzerAsync(source);
    }

    [Fact]
    public async Task DuplicateRoutes_SwitchStatement_NoDiagnostics()
    {
        // Arrange
        var source = @"
using System;
using Microsoft.AspNetCore.Builder;
var app = WebApplication.Create();
switch (Random.Shared.Next())
{
    case 0:
        app.MapGet(""/"", () => Hello());
        return;
    case 1:
        app.MapGet(""/"", () => Hello());
        return;
}
void Hello() { }
";

        // Act & Assert
        await VerifyCS.VerifyAnalyzerAsync(source);
    }

    [Fact]
    public async Task DuplicateRoutes_InsideSwitchStatement_HasDiagnostics()
    {
        // Arrange
        var source = @"
using System;
using Microsoft.AspNetCore.Builder;
var app = WebApplication.Create();
switch (Random.Shared.Next())
{
    case 0:
        app.MapGet({|#0:""/""|}, () => Hello());
        app.MapGet({|#1:""/""|}, () => Hello());
        return;

}
void Hello() { }
";

        var expectedDiagnostics = new[] {
            new DiagnosticResult(DiagnosticDescriptors.AmbiguousRouteHandlerRoute).WithArguments("/").WithLocation(0),
            new DiagnosticResult(DiagnosticDescriptors.AmbiguousRouteHandlerRoute).WithArguments("/").WithLocation(1)
        };

        // Act & Assert
        await VerifyCS.VerifyAnalyzerAsync(source, expectedDiagnostics);
    }

    [Fact]
    public async Task DuplicateRoutes_SwitchExpression_NoDiagnostics()
    {
        // Arrange
        var source = @"
using System;
using Microsoft.AspNetCore.Builder;
var app = WebApplication.Create();
_ = Random.Shared.Next() switch
{
    0 => app.MapGet(""/"", () => Hello()),
    1 => app.MapGet(""/"", () => Hello()),
    _ => throw new Exception()
};
void Hello() { }
";

        // Act & Assert
        await VerifyCS.VerifyAnalyzerAsync(source);
    }

    [Fact]
    public async Task DuplicateRoutes_NullCoalescing_NoDiagnostics()
    {
        // Arrange
        var source = @"
using Microsoft.AspNetCore.Builder;
var app = WebApplication.Create();
_ = app.MapGet(""/"", () => Hello()) ?? app.MapGet(""/"", () => Hello());
void Hello() { }
";

        // Act & Assert
        await VerifyCS.VerifyAnalyzerAsync(source);
    }

    [Fact]
    public async Task DuplicateRoutes_NullCoalescingAssignment_NoDiagnostics()
    {
        // Arrange
        var source = @"
using Microsoft.AspNetCore.Builder;
var app = WebApplication.Create();
var ep = app.MapPost(""/"", () => Hello());
ep ??= app.MapPost(""/"", () => Hello());
void Hello() { }
";

        // Act & Assert
        await VerifyCS.VerifyAnalyzerAsync(source);
    }

    [Fact]
    public async Task DuplicateRoutes_DifferentMethods_HasDiagnostics()
    {
        // Arrange
        var source = @"
using Microsoft.AspNetCore.Builder;
var app = WebApplication.Create();
app.MapGet(""/"", () => Hello());
app.MapPost(""/"", () => Hello());
void Hello() { }
";

        // Act & Assert
        await VerifyCS.VerifyAnalyzerAsync(source);
    }

    [Fact]
    public async Task DuplicateMapGetRoutes_InsideConditional_NoDiagnostics()
    {
        // Arrange
        var source = @"
using Microsoft.AspNetCore.Builder;
var app = WebApplication.Create();
if (true)
{
    app.MapGet(""/"", () => Hello());
}
else
{
    app.MapGet(""/"", () => Hello());
}
void Hello() { }
";

        // Act & Assert
        await VerifyCS.VerifyAnalyzerAsync(source);
    }

    [Fact]
    public async Task DuplicateMapGetRoutes_DuplicatesInsideConditional_NoDiagnostics()
    {
        // Arrange
        var source = @"
using Microsoft.AspNetCore.Builder;
var app = WebApplication.Create();
if (true)
{
    app.MapGet({|#0:""/""|}, () => Hello());
    app.MapGet({|#1:""/""|}, () => Hello());
}
void Hello() { }
";

        var expectedDiagnostics = new[] {
            new DiagnosticResult(DiagnosticDescriptors.AmbiguousRouteHandlerRoute).WithArguments("/").WithLocation(0),
            new DiagnosticResult(DiagnosticDescriptors.AmbiguousRouteHandlerRoute).WithArguments("/").WithLocation(1)
        };

        // Act & Assert
        await VerifyCS.VerifyAnalyzerAsync(source, expectedDiagnostics);
    }

    [Fact]
    public async Task DuplicateRoutes_UnknownUsageOfEndConventionBuilderExtension_NoDiagnostics()
    {
        // Arrange
        var source = @"
using Microsoft.AspNetCore.Builder;
var app = WebApplication.Create();
app.MapGet(""/"", () => Hello()).DoSomething();
app.MapGet(""/"", () => Hello());
void Hello() { }

internal static class Extensions
{
    public static void DoSomething(this IEndpointConventionBuilder builder)
    {
        builder.WithMetadata(new object());
    }
}
";

        // Act & Assert
        await VerifyCS.VerifyAnalyzerAsync(source);
    }

    [Fact]
    public async Task DuplicateRoutes_UnknownUsageOfEndConventionBuilder_NoDiagnostics()
    {
        // Arrange
        var source = @"
using Microsoft.AspNetCore.Builder;
var app = WebApplication.Create();
Extensions.DoSomething(app.MapGet(""/"", () => Hello()));
app.MapGet(""/"", () => Hello());
void Hello() { }

internal static class Extensions
{
    public static void DoSomething(IEndpointConventionBuilder builder)
    {
        builder.WithMetadata(new object());
    }
}
";

        // Act & Assert
        await VerifyCS.VerifyAnalyzerAsync(source);
    }

    [Fact]
    public async Task DuplicateRoutes_AddMethod_NoDiagnostics()
    {
        // Arrange
        var source = @"
using Microsoft.AspNetCore.Builder;
var app = WebApplication.Create();
app.MapGet(""/"", () => Hello()).Add(b => {});
app.MapGet(""/"", () => Hello());
void Hello() { }
";

        // Act & Assert
        await VerifyCS.VerifyAnalyzerAsync(source);
    }

    [Fact]
    public async Task DuplicateRoutes_AssignedToVariable_NoDiagnostics()
    {
        // Arrange
        var source = @"
using Microsoft.AspNetCore.Builder;
var app = WebApplication.Create();
_ = app.MapGet(""/"", () => Hello());
app.MapGet(""/"", () => Hello());
void Hello() { }
";

        // Act & Assert
        await VerifyCS.VerifyAnalyzerAsync(source);
    }

    [Fact]
    public async Task DuplicateRoutes_MultipleGroups_NoDiagnostics()
    {
        // Arrange
        var source = @"
using Microsoft.AspNetCore.Builder;
var app = WebApplication.Create();
var group1 = app.MapGroup(""/group1"");
var group2 = app.MapGroup(""/group2"");
group1.MapGet(""/"", () => Hello());
group2.MapGet(""/"", () => Hello());
void Hello() { }
";

        // Act & Assert
        await VerifyCS.VerifyAnalyzerAsync(source);
    }

    [Fact]
    public async Task DuplicateRoutes_EndpointsOnGroup_HasDiagnostics()
    {
        // Arrange
        var source = @"
using Microsoft.AspNetCore.Builder;
var app = WebApplication.Create();
var group1 = app.MapGroup(""/group1"");
group1.MapGet({|#0:""/""|}, () => Hello());
group1.MapGet({|#1:""/""|}, () => Hello());
var group2 = app.MapGroup(""/group2"");
group2.MapGet(""/"", () => Hello());
void Hello() { }
";

        // Act & Assert
        var expectedDiagnostics = new[] {
            new DiagnosticResult(DiagnosticDescriptors.AmbiguousRouteHandlerRoute).WithArguments("/").WithLocation(0),
            new DiagnosticResult(DiagnosticDescriptors.AmbiguousRouteHandlerRoute).WithArguments("/").WithLocation(1)
        };

        await VerifyCS.VerifyAnalyzerAsync(source, expectedDiagnostics);
    }

    [Theory]
    [InlineData(@"RequireAuthorization()")]
    [InlineData(@"AllowAnonymous()")]
    [InlineData(@"Produces(statusCode:420)")]
    [InlineData(@"WithDisplayName(""test!"")")]
    [InlineData(@"WithName(""test!"")")]
    [InlineData(@"RequireCors(""test!"")")]
    [InlineData(@"CacheOutput(""test!"")")]
    [InlineData(@"DisableRateLimiting()")]
    [InlineData(@"RequireAuthorization().DisableRateLimiting()")]
    public async Task DuplicateRoutes_AllowedBuilderExtensionMethods_HasDiagnostics(string method)
    {
        // Arrange
        var source = @"
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.DependencyInjection;
var app = WebApplication.Create();
app.MapGet({|#0:""/""|}, () => Hello())." + method + @";
app.MapGet({|#1:""/""|}, () => Hello());
void Hello() { }
";

        var expectedDiagnostics = new[] {
            new DiagnosticResult(DiagnosticDescriptors.AmbiguousRouteHandlerRoute).WithArguments("/").WithLocation(0),
            new DiagnosticResult(DiagnosticDescriptors.AmbiguousRouteHandlerRoute).WithArguments("/").WithLocation(1)
        };

        // Act & Assert
        await VerifyCS.VerifyAnalyzerAsync(source, expectedDiagnostics);
    }

    [Theory]
    [InlineData(@"RequireHost(""test!"")")]
    [InlineData(@"RequireHost(""test!"").DisableRateLimiting()")]
    [InlineData(@"RequireAuthorization().RequireHost(""test!"")")]
    public async Task DuplicateRoutes_UnknownBuilderExtensionMethods_NoDiagnostics(string method)
    {
        // Arrange
        var source = @"
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.DependencyInjection;
var app = WebApplication.Create();
app.MapGet({|#0:""/""|}, () => Hello())." + method + @";
app.MapGet({|#1:""/""|}, () => Hello());
void Hello() { }
";

        // Act & Assert
        await VerifyCS.VerifyAnalyzerAsync(source);
    }
}

