using FluentValidation;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using SIGCM2.Api.Authorization;
using SIGCM2.Application.Abstractions;
using SIGCM2.Application.Roles.Create;
using SIGCM2.Application.Roles.Deactivate;
using SIGCM2.Application.Roles.Dtos;
using SIGCM2.Application.Roles.Get;
using SIGCM2.Application.Roles.List;
using SIGCM2.Application.Roles.Update;

namespace SIGCM2.Api.Controllers;

[ApiController]
[Route("api/v1/roles")]
[RequirePermission("administracion:roles:gestionar")]
public sealed class RolesController : ControllerBase
{
    private readonly IDispatcher _dispatcher;
    private readonly IValidator<CreateRolCommand> _createValidator;
    private readonly IValidator<UpdateRolCommand> _updateValidator;

    public RolesController(
        IDispatcher dispatcher,
        IValidator<CreateRolCommand> createValidator,
        IValidator<UpdateRolCommand> updateValidator)
    {
        _dispatcher = dispatcher;
        _createValidator = createValidator;
        _updateValidator = updateValidator;
    }

    /// <summary>Lists all roles (including inactive). Requires admin role.</summary>
    [HttpGet]
    [ProducesResponseType(typeof(IReadOnlyList<RolDto>), StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status401Unauthorized)]
    [ProducesResponseType(StatusCodes.Status403Forbidden)]
    public async Task<IActionResult> List()
    {
        var result = await _dispatcher.Send<ListRolesQuery, IReadOnlyList<RolDto>>(new ListRolesQuery());
        return Ok(result);
    }

    /// <summary>Gets a role by its code. Requires admin role.</summary>
    [HttpGet("{codigo}")]
    [ProducesResponseType(typeof(RolDto), StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status401Unauthorized)]
    [ProducesResponseType(StatusCodes.Status403Forbidden)]
    [ProducesResponseType(StatusCodes.Status404NotFound)]
    public async Task<IActionResult> GetByCodigo(string codigo)
    {
        var result = await _dispatcher.Send<GetRolByCodigoQuery, RolDto>(new GetRolByCodigoQuery(codigo));
        return Ok(result);
    }

    /// <summary>Creates a new role. Requires admin role.</summary>
    [HttpPost]
    [ProducesResponseType(typeof(RolCreatedDto), StatusCodes.Status201Created)]
    [ProducesResponseType(StatusCodes.Status400BadRequest)]
    [ProducesResponseType(StatusCodes.Status401Unauthorized)]
    [ProducesResponseType(StatusCodes.Status403Forbidden)]
    [ProducesResponseType(StatusCodes.Status409Conflict)]
    public async Task<IActionResult> Create([FromBody] CreateRolRequest request)
    {
        var command = new CreateRolCommand(
            Codigo: request.Codigo ?? string.Empty,
            Nombre: request.Nombre ?? string.Empty,
            Descripcion: request.Descripcion);

        var validation = await _createValidator.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<CreateRolCommand, RolCreatedDto>(command);
        return CreatedAtAction(nameof(GetByCodigo), new { codigo = result.Codigo }, result);
    }

    /// <summary>Updates a role (codigo is immutable; route wins over body). Requires admin role.</summary>
    [HttpPut("{codigo}")]
    [ProducesResponseType(typeof(RolDto), StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status400BadRequest)]
    [ProducesResponseType(StatusCodes.Status401Unauthorized)]
    [ProducesResponseType(StatusCodes.Status403Forbidden)]
    [ProducesResponseType(StatusCodes.Status404NotFound)]
    public async Task<IActionResult> Update(string codigo, [FromBody] UpdateRolRequest request)
    {
        // Codigo comes from the route — body.codigo (if present) is ignored by design.
        var command = new UpdateRolCommand(
            Codigo: codigo,
            Nombre: request.Nombre ?? string.Empty,
            Descripcion: request.Descripcion,
            Activo: request.Activo);

        var validation = await _updateValidator.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<UpdateRolCommand, RolDto>(command);
        return Ok(result);
    }

    /// <summary>Soft-deletes (deactivates) a role. 409 if active usuarios reference it. Requires admin role.</summary>
    [HttpDelete("{codigo}")]
    [ProducesResponseType(StatusCodes.Status204NoContent)]
    [ProducesResponseType(StatusCodes.Status401Unauthorized)]
    [ProducesResponseType(StatusCodes.Status403Forbidden)]
    [ProducesResponseType(StatusCodes.Status404NotFound)]
    [ProducesResponseType(StatusCodes.Status409Conflict)]
    public async Task<IActionResult> Deactivate(string codigo)
    {
        await _dispatcher.Send<DeactivateRolCommand, RolDto>(new DeactivateRolCommand(codigo));
        return NoContent();
    }
}

public sealed record CreateRolRequest(string? Codigo, string? Nombre, string? Descripcion);
public sealed record UpdateRolRequest(string? Nombre, string? Descripcion, bool Activo);
