using FluentValidation;
using Microsoft.AspNetCore.Mvc;
using SIGCM2.Api.Authorization;
using SIGCM2.Application.Abstractions;
using SIGCM2.Application.Common;
using SIGCM2.Application.Pricing.ChargeableChars;
using SIGCM2.Application.Pricing.ChargeableChars.Create;
using SIGCM2.Application.Pricing.ChargeableChars.Deactivate;
using SIGCM2.Application.Pricing.ChargeableChars.Delete;
using SIGCM2.Application.Pricing.ChargeableChars.GetById;
using SIGCM2.Application.Pricing.ChargeableChars.List;
using SIGCM2.Application.Pricing.ChargeableChars.Reactivate;
using SIGCM2.Application.Pricing.ChargeableChars.SchedulePrice;

namespace SIGCM2.Api.Controllers;

/// <summary>
/// PRC-001: Admin endpoints for ChargeableCharConfig management.
/// All endpoints require 'tasacion:caracteres_especiales:gestionar'.
/// Route base: api/v1/admin/chargeable-chars
/// </summary>
[ApiController]
[Route("api/v1/admin/chargeable-chars")]
public sealed class ChargeableCharConfigController : ControllerBase
{
    private readonly IDispatcher _dispatcher;
    private readonly IValidator<CreateChargeableCharConfigCommand> _createValidator;
    private readonly IValidator<SchedulePriceChangeCommand> _scheduleValidator;

    public ChargeableCharConfigController(
        IDispatcher dispatcher,
        IValidator<CreateChargeableCharConfigCommand> createValidator,
        IValidator<SchedulePriceChangeCommand> scheduleValidator)
    {
        _dispatcher = dispatcher;
        _createValidator = createValidator;
        _scheduleValidator = scheduleValidator;
    }

    // ── GET /api/v1/admin/chargeable-chars ────────────────────────────────────

    /// <summary>
    /// Returns a paginated list of ChargeableCharConfig rows.
    /// Filters: productTypeId (optional, long?), activeOnly (bool, default true).
    /// Pagination: skip/take model mapped to page/pageSize — or use page/pageSize directly.
    /// Defaults: page=1, pageSize=20. Clamped: pageSize max 200.
    /// </summary>
    [HttpGet]
    [RequirePermission("tasacion:caracteres_especiales:gestionar")]
    [ProducesResponseType(typeof(PagedResult<ChargeableCharConfigDto>), StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status401Unauthorized)]
    [ProducesResponseType(StatusCodes.Status403Forbidden)]
    public async Task<IActionResult> List(
        [FromQuery] long? productTypeId,
        [FromQuery] bool activeOnly = true,
        [FromQuery] int? page = null,
        [FromQuery] int? pageSize = null,
        [FromQuery] int? skip = null,
        [FromQuery] int? take = null)
    {
        // Support both page/pageSize and skip/take query patterns
        int resolvedPage;
        int resolvedPageSize;

        if (skip is not null || take is not null)
        {
            // Convert skip/take to page/pageSize
            resolvedPageSize = Math.Min(take ?? 50, 200);
            resolvedPage = resolvedPageSize > 0
                ? ((skip ?? 0) / resolvedPageSize) + 1
                : 1;
        }
        else
        {
            resolvedPage = page ?? 1;
            resolvedPageSize = Math.Min(pageSize ?? 20, 200);
        }

        var query = new ListChargeableCharConfigQuery(productTypeId, activeOnly, resolvedPage, resolvedPageSize);
        var result = await _dispatcher.Send<ListChargeableCharConfigQuery, PagedResult<ChargeableCharConfigDto>>(query);
        return Ok(result);
    }

    // ── GET /api/v1/admin/chargeable-chars/{id} ───────────────────────────────

    /// <summary>
    /// Returns a single ChargeableCharConfig by Id. Returns 404 if not found.
    /// </summary>
    [HttpGet("{id:long}")]
    [RequirePermission("tasacion:caracteres_especiales:gestionar")]
    [ProducesResponseType(typeof(ChargeableCharConfigDto), StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status401Unauthorized)]
    [ProducesResponseType(StatusCodes.Status403Forbidden)]
    [ProducesResponseType(StatusCodes.Status404NotFound)]
    public async Task<IActionResult> GetById([FromRoute] long id)
    {
        var result = await _dispatcher.Send<GetChargeableCharConfigByIdQuery, ChargeableCharConfigDto?>(
            new GetChargeableCharConfigByIdQuery(id));
        return result is null ? NotFound() : Ok(result);
    }

    // ── POST /api/v1/admin/chargeable-chars ───────────────────────────────────

    /// <summary>
    /// Creates a new ChargeableCharConfig row. Closes the current active row for (ProductTypeId, Symbol) if one exists.
    /// Returns 201 Created with Location header pointing to GET /{id}.
    /// </summary>
    [HttpPost]
    [RequirePermission("tasacion:caracteres_especiales:gestionar")]
    [ProducesResponseType(typeof(CreateChargeableCharConfigResponse), StatusCodes.Status201Created)]
    [ProducesResponseType(StatusCodes.Status400BadRequest)]
    [ProducesResponseType(StatusCodes.Status401Unauthorized)]
    [ProducesResponseType(StatusCodes.Status403Forbidden)]
    [ProducesResponseType(StatusCodes.Status409Conflict)]
    public async Task<IActionResult> Create([FromBody] CreateChargeableCharConfigRequest request)
    {
        var command = new CreateChargeableCharConfigCommand(
            request.ProductTypeId,
            request.Symbol,
            request.Category,
            request.PricePerUnit,
            request.ValidFrom);

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

    // ── PUT /api/v1/admin/chargeable-chars/{id}/price ────────────────────────

    /// <summary>
    /// Schedules a price change for an existing ChargeableCharConfig.
    /// Closes the current active row and opens a new one with the new price + ValidFrom.
    /// ValidFrom must be strictly greater than the existing row's ValidFrom (forward-only).
    /// </summary>
    [HttpPut("{id:long}/price")]
    [RequirePermission("tasacion:caracteres_especiales:gestionar")]
    [ProducesResponseType(typeof(SchedulePriceChangeResponse), StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status400BadRequest)]
    [ProducesResponseType(StatusCodes.Status401Unauthorized)]
    [ProducesResponseType(StatusCodes.Status403Forbidden)]
    [ProducesResponseType(StatusCodes.Status404NotFound)]
    [ProducesResponseType(StatusCodes.Status409Conflict)]
    public async Task<IActionResult> SchedulePriceChange(
        [FromRoute] long id,
        [FromBody] SchedulePriceChangeRequest request)
    {
        var command = new SchedulePriceChangeCommand(id, request.PricePerUnit, request.ValidFrom);

        var validation = await _scheduleValidator.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<SchedulePriceChangeCommand, SchedulePriceChangeResponse>(command);
        return Ok(result);
    }

    // ── PATCH /api/v1/admin/chargeable-chars/{id}/deactivate ─────────────────

    /// <summary>
    /// Deactivates a ChargeableCharConfig row (sets IsActive=false, ValidTo=today_AR).
    /// Idempotent: calling on an already-inactive row is a no-op.
    /// </summary>
    [HttpPatch("{id:long}/deactivate")]
    [RequirePermission("tasacion:caracteres_especiales:gestionar")]
    [ProducesResponseType(typeof(DeactivateChargeableCharConfigResponse), StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status401Unauthorized)]
    [ProducesResponseType(StatusCodes.Status403Forbidden)]
    public async Task<IActionResult> Deactivate([FromRoute] long id)
    {
        var result = await _dispatcher.Send<DeactivateChargeableCharConfigCommand, DeactivateChargeableCharConfigResponse>(
            new DeactivateChargeableCharConfigCommand(id));
        return Ok(result);
    }

    // ── PATCH /api/v1/admin/chargeable-chars/{id}/reactivate ─────────────────

    /// <summary>
    /// Reactivates a previously closed ChargeableCharConfig row (undo last deactivation).
    /// Guard rules (enforced by SP):
    ///   - ALREADY_ACTIVE: target row is already active → 409
    ///   - VIGENTE_EXISTS: a different active row exists for (ProductTypeId, Symbol) → 409
    ///   - POSTERIOR_ROWS_EXIST: rows with higher ValidFrom exist after the target → 409
    /// </summary>
    [HttpPatch("{id:long}/reactivate")]
    [RequirePermission("tasacion:caracteres_especiales:gestionar")]
    [ProducesResponseType(typeof(ReactivateChargeableCharConfigResponse), StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status401Unauthorized)]
    [ProducesResponseType(StatusCodes.Status403Forbidden)]
    [ProducesResponseType(StatusCodes.Status409Conflict)]
    public async Task<IActionResult> Reactivate([FromRoute] long id)
    {
        var result = await _dispatcher.Send<ReactivateChargeableCharConfigCommand, ReactivateChargeableCharConfigResponse>(
            new ReactivateChargeableCharConfigCommand(id));
        return Ok(result);
    }

    // ── DELETE /api/v1/admin/chargeable-chars/{id} ───────────────────────────

    /// <summary>
    /// Deletes a ChargeableCharConfig row.
    /// NOTE: With SYSTEM_VERSIONING ON, the row is moved to the history table (temporal audit preserved).
    /// The row disappears from all current-state queries.
    /// Guard for "used in invoicing" is deferred to FAC-001 followup issue.
    /// Returns 200 + { id } consistent with the Deactivate pattern.
    /// </summary>
    [HttpDelete("{id:long}")]
    [RequirePermission("tasacion:caracteres_especiales:gestionar")]
    [ProducesResponseType(typeof(DeleteChargeableCharConfigResponse), StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status401Unauthorized)]
    [ProducesResponseType(StatusCodes.Status403Forbidden)]
    [ProducesResponseType(StatusCodes.Status404NotFound)]
    public async Task<IActionResult> Delete([FromRoute] long id)
    {
        var result = await _dispatcher.Send<DeleteChargeableCharConfigCommand, DeleteChargeableCharConfigResponse>(
            new DeleteChargeableCharConfigCommand(id));
        return Ok(result);
    }
}

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

/// <summary>PRC-001: Create ChargeableCharConfig request body.</summary>
public sealed record CreateChargeableCharConfigRequest(
    long? ProductTypeId,
    string Symbol,
    string Category,
    decimal PricePerUnit,
    DateOnly ValidFrom);

/// <summary>PRC-001: Schedule price change request body.</summary>
public sealed record SchedulePriceChangeRequest(
    decimal PricePerUnit,
    DateOnly ValidFrom);
