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

namespace SIGCM2.Api.Controllers;

/// <summary>
/// ADM-001: Medio management endpoints at /api/v1/admin/medios.
/// All endpoints require permission 'administracion:medios:gestionar'.
/// </summary>
[ApiController]
[Route("api/v1/admin/medios")]
public sealed class MediosController : ControllerBase
{
    private readonly IDispatcher _dispatcher;
    private readonly IValidator<CreateMedioCommand> _createValidator;
    private readonly IValidator<UpdateMedioCommand> _updateValidator;

    public MediosController(
        IDispatcher dispatcher,
        IValidator<CreateMedioCommand> createValidator,
        IValidator<UpdateMedioCommand> updateValidator)
    {
        _dispatcher = dispatcher;
        _createValidator = createValidator;
        _updateValidator = updateValidator;
    }

    /// <summary>Creates a new medio. Requires administracion:medios:gestionar.</summary>
    [HttpPost]
    [RequirePermission("administracion:medios:gestionar")]
    [ProducesResponseType(typeof(MedioCreatedDto), StatusCodes.Status201Created)]
    [ProducesResponseType(StatusCodes.Status400BadRequest)]
    [ProducesResponseType(StatusCodes.Status401Unauthorized)]
    [ProducesResponseType(StatusCodes.Status403Forbidden)]
    [ProducesResponseType(StatusCodes.Status409Conflict)]
    public async Task<IActionResult> CreateMedio([FromBody] CreateMedioRequest request)
    {
        var command = new CreateMedioCommand(
            Codigo: request.Codigo ?? string.Empty,
            Nombre: request.Nombre ?? string.Empty,
            Tipo: request.Tipo ?? TipoMedio.Diario,
            PlataformaEmpresaId: request.PlataformaEmpresaId);

        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<CreateMedioCommand, MedioCreatedDto>(command);
        return CreatedAtAction(nameof(GetMedioById), new { id = result.Id }, result);
    }

    /// <summary>Lists medios with optional filters and pagination.</summary>
    [HttpGet]
    [RequirePermission("administracion:medios:gestionar")]
    [ProducesResponseType(typeof(PagedResult<MedioListItemDto>), StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status401Unauthorized)]
    [ProducesResponseType(StatusCodes.Status403Forbidden)]
    public async Task<IActionResult> ListMedios(
        [FromQuery] int page = 1,
        [FromQuery] int pageSize = 20,
        [FromQuery] bool? activo = null,
        [FromQuery] TipoMedio? tipo = null,
        [FromQuery] string? q = 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 ListMediosQuery(page, pageSize, activo, tipo, q);
        var result = await _dispatcher.Send<ListMediosQuery, PagedResult<MedioListItemDto>>(query);
        return Ok(result);
    }

    /// <summary>Gets a single medio by id.</summary>
    [HttpGet("{id:int}")]
    [RequirePermission("administracion:medios:gestionar")]
    [ProducesResponseType(typeof(MedioDetailDto), StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status401Unauthorized)]
    [ProducesResponseType(StatusCodes.Status403Forbidden)]
    [ProducesResponseType(StatusCodes.Status404NotFound)]
    public async Task<IActionResult> GetMedioById([FromRoute] int id)
    {
        var query = new GetMedioByIdQuery(id);
        var result = await _dispatcher.Send<GetMedioByIdQuery, MedioDetailDto>(query);
        return Ok(result);
    }

    /// <summary>Updates a medio's editable fields.</summary>
    [HttpPut("{id:int}")]
    [RequirePermission("administracion:medios:gestionar")]
    [ProducesResponseType(typeof(MedioUpdatedDto), StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status400BadRequest)]
    [ProducesResponseType(StatusCodes.Status401Unauthorized)]
    [ProducesResponseType(StatusCodes.Status403Forbidden)]
    [ProducesResponseType(StatusCodes.Status404NotFound)]
    public async Task<IActionResult> UpdateMedio([FromRoute] int id, [FromBody] UpdateMedioRequest request)
    {
        var command = new UpdateMedioCommand(
            Id: id,
            Nombre: request.Nombre ?? string.Empty,
            Tipo: request.Tipo ?? TipoMedio.Diario,
            PlataformaEmpresaId: request.PlataformaEmpresaId);

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

    /// <summary>Deactivates a medio (idempotent).</summary>
    [HttpPost("{id:int}/deactivate")]
    [RequirePermission("administracion:medios:gestionar")]
    [ProducesResponseType(StatusCodes.Status204NoContent)]
    [ProducesResponseType(StatusCodes.Status401Unauthorized)]
    [ProducesResponseType(StatusCodes.Status403Forbidden)]
    [ProducesResponseType(StatusCodes.Status404NotFound)]
    public async Task<IActionResult> DeactivateMedio([FromRoute] int id)
    {
        var command = new DeactivateMedioCommand(id);
        await _dispatcher.Send<DeactivateMedioCommand, MedioStatusDto>(command);
        return NoContent();
    }

    /// <summary>Reactivates a medio (idempotent).</summary>
    [HttpPost("{id:int}/reactivate")]
    [RequirePermission("administracion:medios:gestionar")]
    [ProducesResponseType(StatusCodes.Status204NoContent)]
    [ProducesResponseType(StatusCodes.Status401Unauthorized)]
    [ProducesResponseType(StatusCodes.Status403Forbidden)]
    [ProducesResponseType(StatusCodes.Status404NotFound)]
    public async Task<IActionResult> ReactivateMedio([FromRoute] int id)
    {
        var command = new ReactivateMedioCommand(id);
        await _dispatcher.Send<ReactivateMedioCommand, MedioStatusDto>(command);
        return NoContent();
    }
}

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

/// <summary>ADM-001: Create medio request body.</summary>
public sealed record CreateMedioRequest(
    string? Codigo,
    string? Nombre,
    TipoMedio? Tipo,
    int? PlataformaEmpresaId);

/// <summary>ADM-001: Update medio request body.</summary>
public sealed record UpdateMedioRequest(
    string? Nombre,
    TipoMedio? Tipo,
    int? PlataformaEmpresaId);
