using FluentValidation;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using SIGCM2.Api.Authorization;
using SIGCM2.Application.Abstractions;
using SIGCM2.Application.Permisos.Assign;
using SIGCM2.Application.Permisos.Dtos;
using SIGCM2.Application.Permisos.GetByRol;
using SIGCM2.Application.Permisos.List;

namespace SIGCM2.Api.Controllers;

/// <summary>
/// Permisos controller — granular permission per method (UDT-006).
/// [Authorize] at class level requires a valid JWT; each method declares its specific permission.
/// </summary>
[ApiController]
[Route("api/v1")]
[Authorize]  // JWT required on all methods; per-method [RequirePermission] handles authz
public sealed class PermisosController : ControllerBase
{
    private readonly IDispatcher _dispatcher;
    private readonly IValidator<AssignPermisosToRolCommand> _assignValidator;
    private readonly IValidator<GetRolPermisosQuery> _getRolPermisosValidator;

    public PermisosController(
        IDispatcher dispatcher,
        IValidator<AssignPermisosToRolCommand> assignValidator,
        IValidator<GetRolPermisosQuery> getRolPermisosValidator)
    {
        _dispatcher = dispatcher;
        _assignValidator = assignValidator;
        _getRolPermisosValidator = getRolPermisosValidator;
    }

    /// <summary>Lists all permisos in the canonical catalog.</summary>
    [HttpGet("permisos")]
    [RequirePermission("administracion:permisos:ver")]
    [ProducesResponseType(typeof(IReadOnlyList<PermisoDto>), StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status401Unauthorized)]
    [ProducesResponseType(StatusCodes.Status403Forbidden)]
    public async Task<IActionResult> ListPermisos()
    {
        var result = await _dispatcher.Send<ListPermisosQuery, IReadOnlyList<PermisoDto>>(new ListPermisosQuery());
        return Ok(result);
    }

    /// <summary>Gets all permisos assigned to a rol.</summary>
    [HttpGet("roles/{codigo}/permisos")]
    [RequirePermission("administracion:roles_permisos:gestionar", "administracion:permisos:ver")]
    [ProducesResponseType(typeof(IReadOnlyList<PermisoDto>), StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status400BadRequest)]
    [ProducesResponseType(StatusCodes.Status401Unauthorized)]
    [ProducesResponseType(StatusCodes.Status403Forbidden)]
    [ProducesResponseType(StatusCodes.Status404NotFound)]
    public async Task<IActionResult> GetRolPermisos(string codigo)
    {
        var query = new GetRolPermisosQuery(codigo);
        var validation = await _getRolPermisosValidator.ValidateAsync(query);
        if (!validation.IsValid)
        {
            var errors = validation.Errors
                .GroupBy(e => e.PropertyName)
                .ToDictionary(g => g.Key, g => g.Select(e => e.ErrorMessage).ToArray());
            return BadRequest(new { errors });
        }

        var result = await _dispatcher.Send<GetRolPermisosQuery, IReadOnlyList<PermisoDto>>(query);
        return Ok(result);
    }

    /// <summary>
    /// Replace-set: replaces the full permiso assignment for a rol.
    /// Returns the updated permiso set (200).
    /// </summary>
    [HttpPut("roles/{codigo}/permisos")]
    [RequirePermission("administracion:roles_permisos:gestionar")]
    [ProducesResponseType(typeof(IReadOnlyList<PermisoDto>), StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status400BadRequest)]
    [ProducesResponseType(StatusCodes.Status401Unauthorized)]
    [ProducesResponseType(StatusCodes.Status403Forbidden)]
    [ProducesResponseType(StatusCodes.Status404NotFound)]
    public async Task<IActionResult> AssignPermisos(string codigo, [FromBody] AssignPermisosRequest request)
    {
        var codigos = request.Codigos ?? [];
        var command = new AssignPermisosToRolCommand(
            RolCodigo: codigo,
            Codigos: codigos);

        var validation = await _assignValidator.ValidateAsync(command);
        if (!validation.IsValid)
        {
            var errors = validation.Errors
                .GroupBy(e => e.PropertyName)
                .ToDictionary(g => g.Key, g => g.Select(e => e.ErrorMessage).ToArray());
            return BadRequest(new { errors });
        }

        var result = await _dispatcher.Send<AssignPermisosToRolCommand, IReadOnlyList<PermisoDto>>(command);
        return Ok(result);
    }
}

public sealed record AssignPermisosRequest(IReadOnlyList<string>? Codigos);
