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

namespace ERPCore.Controllers;

[Route("api/v1/sales-invoices")]
public sealed class SalesInvoicesController : ApiControllerBase
{
    private readonly ISalesInvoiceService _invoices;
    private readonly ISalesInvoicePaymentService _payments;

    public SalesInvoicesController(ISalesInvoiceService invoices, ISalesInvoicePaymentService payments)
    {
        _invoices = invoices;
        _payments = payments;
    }

    [HttpGet]
    [ProducesResponseType(typeof(PagedResponse<SalesInvoiceSummaryDto>), StatusCodes.Status200OK)]
    public async Task<ActionResult<PagedResponse<SalesInvoiceSummaryDto>>> List(
        [FromQuery] PageQuery query,
        [FromQuery] SalesInvoiceStatus? status,
        [FromQuery] int? customerId,
        [FromQuery] int? warehouseId,
        CancellationToken ct)
        => Ok(await _invoices.ListAsync(query, status, customerId, warehouseId, ct));

    [HttpGet("{salesInvoiceId:int}")]
    [ProducesResponseType(typeof(SalesInvoiceDto), StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status404NotFound)]
    public async Task<ActionResult<SalesInvoiceDto>> GetById(int salesInvoiceId, CancellationToken ct)
    {
        var result = await _invoices.GetAsync(salesInvoiceId, ct);
        if (result is null) return NotFound();
        SetETag(result.RowVersion);
        return Ok(result.Value);
    }

    [HttpGet("{salesInvoiceId:int}/posting-check")]
    [ProducesResponseType(typeof(SalesInvoicePostingCheckDto), StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status404NotFound)]
    public async Task<ActionResult<SalesInvoicePostingCheckDto>> PostingCheck(int salesInvoiceId, CancellationToken ct)
        => Ok(await _invoices.CheckPostingAsync(salesInvoiceId, ct));

    [HttpPost]
    [ProducesResponseType(typeof(SalesInvoiceDto), StatusCodes.Status201Created)]
    public async Task<ActionResult<SalesInvoiceDto>> Create([FromBody] CreateSalesInvoiceRequest request, CancellationToken ct)
    {
        var result = await _invoices.CreateAsync(request, ct);
        SetETag(result.RowVersion);
        return Created($"/api/v1/sales-invoices/{result.Value.SalesInvoiceId}", result.Value);
    }

    [HttpPut("{salesInvoiceId:int}")]
    [ProducesResponseType(typeof(SalesInvoiceDto), StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status404NotFound)]
    [ProducesResponseType(StatusCodes.Status412PreconditionFailed)]
    public async Task<ActionResult<SalesInvoiceDto>> Update(int salesInvoiceId, [FromBody] UpdateSalesInvoiceRequest request, CancellationToken ct)
    {
        var expected = RequireIfMatch();
        var result = await _invoices.UpdateAsync(salesInvoiceId, request, expected, ct);
        SetETag(result.RowVersion);
        return Ok(result.Value);
    }

    [HttpPost("{salesInvoiceId:int}/post")]
    [ProducesResponseType(typeof(SalesInvoiceDto), StatusCodes.Status200OK)]
    public async Task<ActionResult<SalesInvoiceDto>> Post(int salesInvoiceId, CancellationToken ct)
        => Ok(await _invoices.PostAsync(salesInvoiceId, ct));

    [HttpPost("{salesInvoiceId:int}/cancel")]
    [ProducesResponseType(typeof(SalesInvoiceDto), StatusCodes.Status200OK)]
    public async Task<ActionResult<SalesInvoiceDto>> Cancel(int salesInvoiceId, CancellationToken ct)
        => Ok(await _invoices.CancelAsync(salesInvoiceId, ct));

    /// <summary>Pay the customer's balance against this invoice, in full or in installments; posts a real GL journal entry.</summary>
    [HttpPost("{salesInvoiceId:int}/payments")]
    [ProducesResponseType(typeof(SalesInvoicePaymentDto), StatusCodes.Status201Created)]
    [ProducesResponseType(StatusCodes.Status400BadRequest)]
    [ProducesResponseType(StatusCodes.Status404NotFound)]
    [ProducesResponseType(StatusCodes.Status409Conflict)]
    public async Task<ActionResult<SalesInvoicePaymentDto>> Pay(int salesInvoiceId, [FromBody] CreateSalesInvoicePaymentRequest request, CancellationToken ct)
    {
        var dto = await _payments.PayAsync(salesInvoiceId, request, ct);
        return Created($"/api/v1/sales-invoices/{salesInvoiceId}/payments/{dto.SalesInvoicePaymentId}", dto);
    }

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