using ERPCore.Domain.Enums;
using ERPCore.Dtos.Common;
using ERPCore.Dtos.Grn;
using ERPCore.Services.Interfaces;
using Microsoft.AspNetCore.Mvc;

namespace ERPCore.Controllers;

/// <summary>Goods-receipt endpoints (docs/11 §4).</summary>
[Route("api/v1/grns")]
public sealed class GrnsController : ApiControllerBase
{
    private readonly IGrnService _grns;
    private readonly IGrnPaymentService _payments;

    public GrnsController(IGrnService grns, IGrnPaymentService payments)
    {
        _grns = grns;
        _payments = payments;
    }

    /// <summary>List GRNs, newest first.</summary>
    [HttpGet]
    [ProducesResponseType(typeof(PagedResponse<GrnSummaryDto>), StatusCodes.Status200OK)]
    public async Task<ActionResult<PagedResponse<GrnSummaryDto>>> List(
        [FromQuery] PageQuery query, [FromQuery] GrnStatus? status, [FromQuery] int? poId,
        [FromQuery] int? vendorId, [FromQuery] int? warehouseId, CancellationToken ct)
        => Ok(await _grns.ListAsync(query, status, poId, vendorId, warehouseId, ct));

    [HttpGet("{grnId:int}")]
    [ProducesResponseType(typeof(GrnDto), StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status404NotFound)]
    public async Task<ActionResult<GrnDto>> GetById(int grnId, CancellationToken ct)
    {
        var dto = await _grns.GetAsync(grnId, ct);
        return dto is null ? NotFound() : Ok(dto);
    }

    /// <summary>Create a Draft GRN against a PO or direct. Cost is PO-derived for PO lines.</summary>
    [HttpPost]
    [ProducesResponseType(typeof(GrnDto), StatusCodes.Status201Created)]
    [ProducesResponseType(StatusCodes.Status409Conflict)]
    [ProducesResponseType(StatusCodes.Status422UnprocessableEntity)]
    public async Task<ActionResult<GrnDto>> Create([FromBody] CreateGrnRequest request, CancellationToken ct)
    {
        var dto = await _grns.CreateAsync(request, ct);
        return Created($"/api/v1/grns/{dto.GrnId}", dto);
    }

    /// <summary>Confirm: create FIFO layers + inbound ledger + update PO receipts (single UoW txn).</summary>
    [HttpPost("{grnId:int}/confirm")]
    [ProducesResponseType(typeof(GrnConfirmResultDto), StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status404NotFound)]
    [ProducesResponseType(StatusCodes.Status409Conflict)]
    public async Task<ActionResult<GrnConfirmResultDto>> Confirm(
        int grnId, [FromHeader(Name = "Idempotency-Key")] string? idempotencyKey, CancellationToken ct)
        => Ok(await _grns.ConfirmAsync(grnId, idempotencyKey, ct));

    /// <summary>Release or reject an inspection-hold line (FR-GRN-05).</summary>
    [HttpPost("{grnId:int}/lines/{grnLineId:int}/release")]
    [ProducesResponseType(typeof(ReleaseLineResultDto), StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status404NotFound)]
    [ProducesResponseType(StatusCodes.Status409Conflict)]
    public async Task<ActionResult<ReleaseLineResultDto>> Release(
        int grnId, int grnLineId, [FromBody] ReleaseLineRequest request, CancellationToken ct)
        => Ok(await _grns.ReleaseLineAsync(grnId, grnLineId, request.Action, ct));

    /// <summary>Pay the vendor against this GRN's balance, in full or in installments; posts a real GL journal entry.</summary>
    [HttpPost("{grnId:int}/payments")]
    [ProducesResponseType(typeof(GrnPaymentDto), StatusCodes.Status201Created)]
    [ProducesResponseType(StatusCodes.Status400BadRequest)]
    [ProducesResponseType(StatusCodes.Status404NotFound)]
    [ProducesResponseType(StatusCodes.Status409Conflict)]
    public async Task<ActionResult<GrnPaymentDto>> Pay(int grnId, [FromBody] CreateGrnPaymentRequest request, CancellationToken ct)
    {
        var dto = await _payments.PayAsync(grnId, request, ct);
        return Created($"/api/v1/grns/{grnId}/payments/{dto.GrnPaymentId}", dto);
    }

    /// <summary>Payment history for this GRN, newest first.</summary>
    [HttpGet("{grnId:int}/payments")]
    [ProducesResponseType(typeof(IReadOnlyList<GrnPaymentDto>), StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status404NotFound)]
    public async Task<ActionResult<IReadOnlyList<GrnPaymentDto>>> ListPayments(int grnId, CancellationToken ct)
        => Ok(await _payments.ListAsync(grnId, ct));
}
