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

namespace SIGCM2.Api.Controllers;

/// <summary>
/// PRD-002: Product catalog management.
/// Read endpoints at /api/v1/products — require authentication (any role).
/// Write endpoints at /api/v1/admin/products — require 'catalogo:productos:gestionar'.
/// </summary>
[ApiController]
public sealed class ProductsController : ControllerBase
{
    private readonly IDispatcher _dispatcher;
    private readonly IValidator<CreateProductCommand> _createValidator;
    private readonly IValidator<UpdateProductCommand> _updateValidator;

    public ProductsController(
        IDispatcher dispatcher,
        IValidator<CreateProductCommand> createValidator,
        IValidator<UpdateProductCommand> updateValidator)
    {
        _dispatcher = dispatcher;
        _createValidator = createValidator;
        _updateValidator = updateValidator;
    }

    // ── READ endpoints ─────────────────────────────────────────────────────────

    /// <summary>Returns a paginated list of Products. Requires authentication.</summary>
    [HttpGet("api/v1/products")]
    [Authorize]
    [ProducesResponseType(typeof(PagedResult<ProductListItemDto>), StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status401Unauthorized)]
    public async Task<IActionResult> ListProducts(
        [FromQuery] int page = 1,
        [FromQuery] int pageSize = 20,
        [FromQuery] bool? activo = true,
        [FromQuery] string? search = null,
        [FromQuery] int? medioId = null,
        [FromQuery] int? productTypeId = null,
        [FromQuery] int? rubroId = null)
    {
        var query = new ListProductsQuery(page, pageSize, activo, search, medioId, productTypeId, rubroId);
        var result = await _dispatcher.Send<ListProductsQuery, PagedResult<ProductListItemDto>>(query);
        return Ok(result);
    }

    /// <summary>Returns a single Product by id. Requires authentication.</summary>
    [HttpGet("api/v1/products/{id:int}")]
    [Authorize]
    [ProducesResponseType(typeof(ProductDetailDto), StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status401Unauthorized)]
    [ProducesResponseType(StatusCodes.Status404NotFound)]
    public async Task<IActionResult> GetProductById([FromRoute] int id)
    {
        var query = new GetProductByIdQuery(id);
        var result = await _dispatcher.Send<GetProductByIdQuery, ProductDetailDto>(query);
        return Ok(result);
    }

    // ── WRITE endpoints ────────────────────────────────────────────────────────

    /// <summary>Creates a new Product. Requires catalogo:productos:gestionar.</summary>
    [HttpPost("api/v1/admin/products")]
    [RequirePermission("catalogo:productos:gestionar")]
    [ProducesResponseType(typeof(ProductCreatedDto), StatusCodes.Status201Created)]
    [ProducesResponseType(StatusCodes.Status400BadRequest)]
    [ProducesResponseType(StatusCodes.Status401Unauthorized)]
    [ProducesResponseType(StatusCodes.Status403Forbidden)]
    [ProducesResponseType(StatusCodes.Status404NotFound)]
    [ProducesResponseType(StatusCodes.Status409Conflict)]
    [ProducesResponseType(StatusCodes.Status422UnprocessableEntity)]
    public async Task<IActionResult> CreateProduct([FromBody] CreateProductRequest request)
    {
        var command = new CreateProductCommand(
            Nombre: request.Nombre ?? string.Empty,
            MedioId: request.MedioId,
            ProductTypeId: request.ProductTypeId,
            RubroId: request.RubroId,
            BasePrice: request.BasePrice,
            PriceDurationDays: request.PriceDurationDays);

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

    /// <summary>Updates a Product. Requires catalogo:productos:gestionar.</summary>
    [HttpPut("api/v1/admin/products/{id:int}")]
    [RequirePermission("catalogo:productos:gestionar")]
    [ProducesResponseType(typeof(ProductUpdatedDto), StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status400BadRequest)]
    [ProducesResponseType(StatusCodes.Status401Unauthorized)]
    [ProducesResponseType(StatusCodes.Status403Forbidden)]
    [ProducesResponseType(StatusCodes.Status404NotFound)]
    [ProducesResponseType(StatusCodes.Status409Conflict)]
    [ProducesResponseType(StatusCodes.Status422UnprocessableEntity)]
    public async Task<IActionResult> UpdateProduct([FromRoute] int id, [FromBody] UpdateProductRequest request)
    {
        var command = new UpdateProductCommand(
            Id: id,
            Nombre: request.Nombre ?? string.Empty,
            RubroId: request.RubroId,
            BasePrice: request.BasePrice,
            PriceDurationDays: request.PriceDurationDays);

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

    /// <summary>Soft-deletes (deactivates) a Product. Requires catalogo:productos:gestionar.</summary>
    [HttpDelete("api/v1/admin/products/{id:int}")]
    [RequirePermission("catalogo:productos:gestionar")]
    [ProducesResponseType(StatusCodes.Status204NoContent)]
    [ProducesResponseType(StatusCodes.Status401Unauthorized)]
    [ProducesResponseType(StatusCodes.Status403Forbidden)]
    [ProducesResponseType(StatusCodes.Status404NotFound)]
    public async Task<IActionResult> DeactivateProduct([FromRoute] int id)
    {
        var command = new DeactivateProductCommand(id);
        await _dispatcher.Send<DeactivateProductCommand, ProductStatusDto>(command);
        return NoContent();
    }
}

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

/// <summary>PRD-002: Create Product request body.</summary>
public sealed record CreateProductRequest(
    string? Nombre,
    int MedioId = 0,
    int ProductTypeId = 0,
    int? RubroId = null,
    decimal BasePrice = 0m,
    int? PriceDurationDays = null);

/// <summary>PRD-002: Update Product request body.</summary>
public sealed record UpdateProductRequest(
    string? Nombre,
    int? RubroId = null,
    decimal BasePrice = 0m,
    int? PriceDurationDays = null);
