using System.Reflection;
using System.Text.RegularExpressions;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Routing;
using Xunit;

namespace JobTrackerApi.Tests;

public sealed class RouteUniquenessTests
{
    [Fact]
    public void Controller_actions_have_one_handler_per_http_method_and_route()
    {
        var actions = typeof(JobTrackerApi.Controllers.JobApplicationsController).Assembly.GetTypes()
            .Where(type => !type.IsAbstract && typeof(ControllerBase).IsAssignableFrom(type))
            .SelectMany(type => type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly)
                .SelectMany(method => method.GetCustomAttributes<HttpMethodAttribute>()
                    .Select(attribute => new
                    {
                        Action = $"{type.Name}.{method.Name}",
                        Methods = string.Join(",", attribute.HttpMethods.OrderBy(x => x)),
                        Route = Normalize(type, attribute.Template),
                    })))
            .ToList();

        var duplicates = actions
            .GroupBy(x => $"{x.Methods} {x.Route}", StringComparer.OrdinalIgnoreCase)
            .Where(group => group.Count() > 1)
            .Select(group => $"{group.Key}: {string.Join(", ", group.Select(x => x.Action))}")
            .ToList();

        Assert.Empty(duplicates);
    }

    private static string Normalize(Type controller, string? actionTemplate)
    {
        var prefix = controller.GetCustomAttribute<RouteAttribute>()?.Template ?? string.Empty;
        prefix = prefix.Replace("[controller]", controller.Name[..^"Controller".Length], StringComparison.OrdinalIgnoreCase);
        var route = $"{prefix.TrimEnd('/')}/{(actionTemplate ?? string.Empty).TrimStart('/')}".ToLowerInvariant();
        return Regex.Replace(route, @"\{[^}:]+(?<constraint>:[^}]+)?\}", match => $"{{parameter{match.Groups["constraint"].Value}}}");
    }
}
