using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using SIGCM2.Api.Authorization;
using SIGCM2.Application.Abstractions;
using SIGCM2.Application.Rubros.Create;
using SIGCM2.Application.Rubros.Deactivate;
using SIGCM2.Application.Rubros.Dtos;
using SIGCM2.Application.Rubros.GetById;
using SIGCM2.Application.Rubros.GetTree;
using SIGCM2.Application.Rubros.Move;
using SIGCM2.Application.Rubros.Update;

namespace SIGCM2.Api.Controllers;

/// <summary>
/// CAT-001: Rubro N-ary tree management.
/// Read endpoints at /api/v1/rubros — require authentication (any role).
/// Write endpoints at /api/v1/admin/rubros — require 'catalogo:rubros:gestionar'.
/// </summary>
[ApiController]
public sealed class RubrosController : ControllerBase
{
    private readonly IDispatcher _dispatcher;

    public RubrosController(IDispatcher dispatcher)
    {
        _dispatcher = dispatcher;
    }

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

    /// <summary>Returns the full Rubro tree. Requires authentication.</summary>
    [HttpGet("api/v1/rubros/tree")]
    [Authorize]
    [ProducesResponseType(typeof(IReadOnlyList<RubroTreeNodeDto>), StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status401Unauthorized)]
    public async Task<IActionResult> GetRubroTree([FromQuery] bool incluirInactivos = false)
    {
        var query = new GetRubroTreeQuery(incluirInactivos);
        var result = await _dispatcher.Send<GetRubroTreeQuery, IReadOnlyList<RubroTreeNodeDto>>(query);
        return Ok(result);
    }

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

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

    /// <summary>Creates a new Rubro. Requires catalogo:rubros:gestionar.</summary>
    [HttpPost("api/v1/admin/rubros")]
    [RequirePermission("catalogo:rubros:gestionar")]
    [ProducesResponseType(typeof(RubroCreatedDto), StatusCodes.Status201Created)]
    [ProducesResponseType(StatusCodes.Status400BadRequest)]
    [ProducesResponseType(StatusCodes.Status401Unauthorized)]
    [ProducesResponseType(StatusCodes.Status403Forbidden)]
    [ProducesResponseType(StatusCodes.Status404NotFound)]
    [ProducesResponseType(StatusCodes.Status409Conflict)]
    [ProducesResponseType(StatusCodes.Status422UnprocessableEntity)]
    public async Task<IActionResult> CreateRubro([FromBody] CreateRubroRequest request)
    {
        var command = new CreateRubroCommand(
            Nombre: request.Nombre ?? string.Empty,
            ParentId: request.ParentId,
            TarifarioBaseId: request.TarifarioBaseId);

        var result = await _dispatcher.Send<CreateRubroCommand, RubroCreatedDto>(command);
        return CreatedAtAction(nameof(GetRubroById), new { id = result.Id }, result);
    }

    /// <summary>Updates a Rubro's nombre. Requires catalogo:rubros:gestionar.</summary>
    [HttpPut("api/v1/admin/rubros/{id:int}")]
    [RequirePermission("catalogo:rubros:gestionar")]
    [ProducesResponseType(typeof(RubroUpdatedDto), StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status400BadRequest)]
    [ProducesResponseType(StatusCodes.Status401Unauthorized)]
    [ProducesResponseType(StatusCodes.Status403Forbidden)]
    [ProducesResponseType(StatusCodes.Status404NotFound)]
    [ProducesResponseType(StatusCodes.Status409Conflict)]
    public async Task<IActionResult> UpdateRubro([FromRoute] int id, [FromBody] UpdateRubroRequest request)
    {
        var command = new UpdateRubroCommand(
            Id: id,
            Nombre: request.Nombre ?? string.Empty);

        var result = await _dispatcher.Send<UpdateRubroCommand, RubroUpdatedDto>(command);
        return Ok(result);
    }

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

    /// <summary>Moves a Rubro to a new parent. Requires catalogo:rubros:gestionar.</summary>
    [HttpPatch("api/v1/admin/rubros/{id:int}/mover")]
    [RequirePermission("catalogo:rubros:gestionar")]
    [ProducesResponseType(typeof(RubroMovedDto), StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status400BadRequest)]
    [ProducesResponseType(StatusCodes.Status401Unauthorized)]
    [ProducesResponseType(StatusCodes.Status403Forbidden)]
    [ProducesResponseType(StatusCodes.Status404NotFound)]
    [ProducesResponseType(StatusCodes.Status409Conflict)]
    [ProducesResponseType(StatusCodes.Status422UnprocessableEntity)]
    public async Task<IActionResult> MoveRubro([FromRoute] int id, [FromBody] MoveRubroRequest request)
    {
        var command = new MoveRubroCommand(
            Id: id,
            NuevoParentId: request.NuevoParentId,
            NuevoOrden: request.NuevoOrden);

        var result = await _dispatcher.Send<MoveRubroCommand, RubroMovedDto>(command);
        return Ok(result);
    }
}

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

/// <summary>CAT-001: Create rubro request body.</summary>
public sealed record CreateRubroRequest(
    string? Nombre,
    int? ParentId,
    int? TarifarioBaseId);

/// <summary>CAT-001: Update rubro request body.</summary>
public sealed record UpdateRubroRequest(
    string? Nombre);

/// <summary>CAT-001: Move rubro request body.</summary>
public sealed record MoveRubroRequest(
    int? NuevoParentId,
    int NuevoOrden);
