using FluentValidation;
using Microsoft.AspNetCore.Mvc;
using SIGCM2.Api.Authorization;
using SIGCM2.Application.Abstractions;
using SIGCM2.Application.Common;
using SIGCM2.Application.PuntosDeVenta.Create;
using SIGCM2.Application.PuntosDeVenta.Deactivate;
using SIGCM2.Application.PuntosDeVenta.GetById;
using SIGCM2.Application.PuntosDeVenta.List;
using SIGCM2.Application.PuntosDeVenta.Reactivate;
using SIGCM2.Application.PuntosDeVenta.Update;

namespace SIGCM2.Api.Controllers;

/// <summary>
/// ADM-008: PuntoDeVenta management endpoints at /api/v1/admin/puntos-de-venta.
/// All endpoints require permission 'administracion:puntos_de_venta:gestionar'.
/// </summary>
[ApiController]
[Route("api/v1/admin/puntos-de-venta")]
public sealed class PuntosDeVentaController : ControllerBase
{
    private readonly IDispatcher _dispatcher;
    private readonly IValidator<CreatePuntoDeVentaCommand> _createValidator;
    private readonly IValidator<UpdatePuntoDeVentaCommand> _updateValidator;

    public PuntosDeVentaController(
        IDispatcher dispatcher,
        IValidator<CreatePuntoDeVentaCommand> createValidator,
        IValidator<UpdatePuntoDeVentaCommand> updateValidator)
    {
        _dispatcher = dispatcher;
        _createValidator = createValidator;
        _updateValidator = updateValidator;
    }

    /// <summary>Creates a new punto de venta. Requires administracion:puntos_de_venta:gestionar.</summary>
    [HttpPost]
    [RequirePermission("administracion:puntos_de_venta:gestionar")]
    [ProducesResponseType(typeof(PuntoDeVentaCreatedDto), StatusCodes.Status201Created)]
    [ProducesResponseType(StatusCodes.Status400BadRequest)]
    [ProducesResponseType(StatusCodes.Status401Unauthorized)]
    [ProducesResponseType(StatusCodes.Status403Forbidden)]
    [ProducesResponseType(StatusCodes.Status404NotFound)]
    [ProducesResponseType(StatusCodes.Status409Conflict)]
    public async Task<IActionResult> CreatePuntoDeVenta([FromBody] CreatePuntoDeVentaRequest request)
    {
        var command = new CreatePuntoDeVentaCommand(
            MedioId: request.MedioId ?? 0,
            NumeroAFIP: request.NumeroAFIP ?? 0,
            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<CreatePuntoDeVentaCommand, PuntoDeVentaCreatedDto>(command);
        return CreatedAtAction(nameof(GetPuntoDeVentaById), new { id = result.Id }, result);
    }

    /// <summary>Lists puntos de venta with optional filters.</summary>
    [HttpGet]
    [RequirePermission("administracion:puntos_de_venta:gestionar")]
    [ProducesResponseType(typeof(PagedResult<PuntoDeVentaListItemDto>), StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status401Unauthorized)]
    [ProducesResponseType(StatusCodes.Status403Forbidden)]
    public async Task<IActionResult> ListPuntosDeVenta(
        [FromQuery] int page = 1,
        [FromQuery] int pageSize = 20,
        [FromQuery] int? medioId = null,
        [FromQuery] bool? activo = null)
    {
        if (page < 1) return BadRequest(new { error = "page must be >= 1" });
        if (pageSize < 1) return BadRequest(new { error = "pageSize must be >= 1" });

        var query = new ListPuntosDeVentaQuery(page, pageSize, medioId, activo);
        var result = await _dispatcher.Send<ListPuntosDeVentaQuery, PagedResult<PuntoDeVentaListItemDto>>(query);
        return Ok(result);
    }

    /// <summary>Gets a single punto de venta by id.</summary>
    [HttpGet("{id:int}")]
    [RequirePermission("administracion:puntos_de_venta:gestionar")]
    [ProducesResponseType(typeof(PuntoDeVentaDetailDto), StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status401Unauthorized)]
    [ProducesResponseType(StatusCodes.Status403Forbidden)]
    [ProducesResponseType(StatusCodes.Status404NotFound)]
    public async Task<IActionResult> GetPuntoDeVentaById([FromRoute] int id)
    {
        var query = new GetPuntoDeVentaByIdQuery(id);
        var result = await _dispatcher.Send<GetPuntoDeVentaByIdQuery, PuntoDeVentaDetailDto>(query);
        return Ok(result);
    }

    /// <summary>Updates a punto de venta's editable fields.</summary>
    [HttpPut("{id:int}")]
    [RequirePermission("administracion:puntos_de_venta:gestionar")]
    [ProducesResponseType(typeof(PuntoDeVentaUpdatedDto), StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status400BadRequest)]
    [ProducesResponseType(StatusCodes.Status401Unauthorized)]
    [ProducesResponseType(StatusCodes.Status403Forbidden)]
    [ProducesResponseType(StatusCodes.Status404NotFound)]
    [ProducesResponseType(StatusCodes.Status409Conflict)]
    public async Task<IActionResult> UpdatePuntoDeVenta([FromRoute] int id, [FromBody] UpdatePuntoDeVentaRequest request)
    {
        var command = new UpdatePuntoDeVentaCommand(
            Id: id,
            Nombre: request.Nombre ?? string.Empty,
            NumeroAFIP: request.NumeroAFIP ?? 0,
            Descripcion: request.Descripcion);

        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<UpdatePuntoDeVentaCommand, PuntoDeVentaUpdatedDto>(command);
        return Ok(result);
    }

    /// <summary>Deactivates a punto de venta.</summary>
    [HttpPost("{id:int}/deactivate")]
    [RequirePermission("administracion:puntos_de_venta:gestionar")]
    [ProducesResponseType(StatusCodes.Status204NoContent)]
    [ProducesResponseType(StatusCodes.Status401Unauthorized)]
    [ProducesResponseType(StatusCodes.Status403Forbidden)]
    [ProducesResponseType(StatusCodes.Status404NotFound)]
    public async Task<IActionResult> DeactivatePuntoDeVenta([FromRoute] int id)
    {
        var command = new DeactivatePuntoDeVentaCommand(id);
        await _dispatcher.Send<DeactivatePuntoDeVentaCommand, PuntoDeVentaStatusDto>(command);
        return NoContent();
    }

    /// <summary>Reactivates a punto de venta (only if parent Medio is active).</summary>
    [HttpPost("{id:int}/reactivate")]
    [RequirePermission("administracion:puntos_de_venta:gestionar")]
    [ProducesResponseType(StatusCodes.Status204NoContent)]
    [ProducesResponseType(StatusCodes.Status401Unauthorized)]
    [ProducesResponseType(StatusCodes.Status403Forbidden)]
    [ProducesResponseType(StatusCodes.Status404NotFound)]
    [ProducesResponseType(StatusCodes.Status409Conflict)]
    public async Task<IActionResult> ReactivatePuntoDeVenta([FromRoute] int id)
    {
        var command = new ReactivatePuntoDeVentaCommand(id);
        await _dispatcher.Send<ReactivatePuntoDeVentaCommand, PuntoDeVentaStatusDto>(command);
        return NoContent();
    }

}

// ── Request body records ──────────────────────────────────────────────────────

/// <summary>ADM-008: Create punto de venta request body.</summary>
public sealed record CreatePuntoDeVentaRequest(
    int? MedioId,
    short? NumeroAFIP,
    string? Nombre,
    string? Descripcion);

/// <summary>ADM-008: Update punto de venta request body.</summary>
public sealed record UpdatePuntoDeVentaRequest(
    string? Nombre,
    short? NumeroAFIP,
    string? Descripcion);
