using FluentValidation;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;

namespace Extrudex.API.Filters;

/// <summary>
/// Action filter that automatically validates request DTOs using FluentValidation
/// validators registered in DI. Runs before the controller action executes.
/// Returns 400 Bad Request with validation errors if validation fails.
/// </summary>
public class FluentValidationFilter : IAsyncActionFilter
{
    private readonly IServiceProvider _serviceProvider;
    private readonly ILogger<FluentValidationFilter> _logger;

    public FluentValidationFilter(IServiceProvider serviceProvider, ILogger<FluentValidationFilter> logger)
    {
        _serviceProvider = serviceProvider;
        _logger = logger;
    }

    public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
    {
        foreach (var argument in context.ActionArguments.Values)
        {
            if (argument is null) continue;

            var argumentType = argument.GetType();
            var validatorType = typeof(IValidator<>).MakeGenericType(argumentType);

            // Try to resolve a validator for this argument type
            var validator = _serviceProvider.GetService(validatorType) as IValidator;
            if (validator is null) continue;

            _logger.LogDebug("Validating {Type} with {Validator}", argumentType.Name, validator.GetType().Name);

            var validationResult = await validator.ValidateAsync(
                new ValidationContext<object>(argument), context.HttpContext.RequestAborted);

            if (!validationResult.IsValid)
            {
                foreach (var error in validationResult.Errors)
                {
                    context.ModelState.AddModelError(error.PropertyName, error.ErrorMessage);
                }
            }
        }

        if (!context.ModelState.IsValid)
        {
            var errors = context.ModelState
                .Where(kvp => kvp.Value?.Errors.Count > 0)
                .ToDictionary(
                    kvp => kvp.Key,
                    kvp => kvp.Value!.Errors.Select(e => e.ErrorMessage).ToArray());

            context.Result = new BadRequestObjectResult(new
            {
                title = "Validation failed",
                status = 400,
                errors
            });
            return;
        }

        await next();
    }
}