using System.Threading.RateLimiting;
using Meezi.API.Security;
using Microsoft.AspNetCore.RateLimiting;

namespace Meezi.API.Extensions;

public static class SecurityExtensions
{
    public static IServiceCollection AddMeeziSecurity(this IServiceCollection services, IConfiguration configuration)
    {
        services.Configure<AbuseProtectionOptions>(configuration.GetSection(AbuseProtectionOptions.SectionName));
        services.AddHttpClient("turnstile");
        services.AddSingleton<IAbuseProtectionService, AbuseProtectionService>();

        services.AddRateLimiter(options =>
        {
            options.RejectionStatusCode = StatusCodes.Status429TooManyRequests;
            options.OnRejected = async (context, token) =>
            {
                context.HttpContext.Response.ContentType = "application/json";
                await context.HttpContext.Response.WriteAsJsonAsync(
                    new
                    {
                        success = false,
                        error = new
                        {
                            code = "RATE_LIMITED",
                            message = "Too many requests. Please wait and try again."
                        }
                    },
                    token);
            };

            options.AddPolicy("public-read", httpContext =>
            {
                var ip = ClientIpResolver.GetClientIp(httpContext);
                var limit = configuration.GetValue("Security:RateLimits:PublicReadsPerIpPerMinute", 120);
                return RateLimitPartition.GetSlidingWindowLimiter(
                    $"read:{ip}",
                    _ => new SlidingWindowRateLimiterOptions
                    {
                        PermitLimit = limit,
                        Window = TimeSpan.FromMinutes(1),
                        SegmentsPerWindow = 4,
                        QueueProcessingOrder = QueueProcessingOrder.OldestFirst,
                        QueueLimit = 0
                    });
            });

            options.AddPolicy("public-write", httpContext =>
            {
                var ip = ClientIpResolver.GetClientIp(httpContext);
                var limit = configuration.GetValue("Security:RateLimits:PublicWritesPerIpPerMinute", 20);
                return RateLimitPartition.GetSlidingWindowLimiter(
                    $"write:{ip}",
                    _ => new SlidingWindowRateLimiterOptions
                    {
                        PermitLimit = limit,
                        Window = TimeSpan.FromMinutes(1),
                        SegmentsPerWindow = 4,
                        QueueProcessingOrder = QueueProcessingOrder.OldestFirst,
                        QueueLimit = 0
                    });
            });

            options.AddPolicy("auth-otp", httpContext =>
            {
                var ip = ClientIpResolver.GetClientIp(httpContext);
                var limit = configuration.GetValue("Security:RateLimits:AuthOtpPerIpPerHour", 15);
                return RateLimitPartition.GetFixedWindowLimiter(
                    $"otp:{ip}",
                    _ => new FixedWindowRateLimiterOptions
                    {
                        PermitLimit = limit,
                        Window = TimeSpan.FromHours(1),
                        QueueProcessingOrder = QueueProcessingOrder.OldestFirst,
                        QueueLimit = 0
                    });
            });
        });

        return services;
    }

    public static void UseMeeziSecurity(this WebApplication app)
    {
        app.UseRateLimiter();
    }
}
