using CashLineERP.Application.Finance;
using CashLineERP.Application.Inventory;
using CashLineERP.Application.Procurement;
using CashLineERP.Contracts.Procurement;
using CashLineERP.Infrastructure.Persistence.Repositories;

namespace CashLineERP.Api.Tests;

public sealed class PurchaseReturnAndDebitNoteServiceQaTests
{
    [Fact]
    public void Purchase_Return_Can_Issue_And_Post_A_Supplier_Debit_Note_With_Traceability()
    {
        using var dbContext = QaDbContextFactory.CreateContext();

        var purchaseReturnService = new PurchaseReturnService(new PurchaseReturnRepository(dbContext));
        var accountsPayableBillService = new AccountsPayableBillService(new AccountsPayableBillRepository(dbContext));
        var inventoryMovementService = new InventoryMovementService(new InventoryMovementReadRepository(dbContext));
        var generalLedgerService = new GeneralLedgerService(new GeneralLedgerReadRepository(dbContext));

        var createdReturn = purchaseReturnService.CreatePurchaseReturn(new CreatePurchaseReturnRequest(
            BranchId: 1,
            SupplierId: 1,
            PurchaseOrderId: 1,
            GoodsReceiptId: 1,
            ReturnDate: new DateOnly(2026, 5, 7),
            ReturnWarehouseId: 1,
            ReasonCode: "SUPPLIER_REJECTION",
            Notes: "Damaged supplier items returned.",
            Lines:
            [
                new CreatePurchaseReturnLineRequest(1, 1, 1001, 5m, null, "BATCH-1001-A", null, "Returned damaged units")
            ]));

        var submittedReturn = purchaseReturnService.SubmitPurchaseReturn(createdReturn.PurchaseReturnId, 3);
        Assert.Equal("SUBMITTED", submittedReturn.StatusCode);

        var postedReturn = purchaseReturnService.PostPurchaseReturn(createdReturn.PurchaseReturnId, 4);
        Assert.Equal("POSTED", postedReturn.StatusCode);

        var returnMovement = inventoryMovementService.GetInventoryMovements()
            .Single(movement => movement.MovementType == "PURCHASE_RETURN_ISSUE" && movement.QuantityMoved == 5m);
        Assert.Equal(1, returnMovement.FromWarehouseId);

        var issuedDebitNote = purchaseReturnService.IssueDebitNote(createdReturn.PurchaseReturnId, new IssuePurchaseReturnDebitNoteRequest(
            ActorUserId: 4,
            BillDate: new DateOnly(2026, 5, 7),
            DueDate: new DateOnly(2026, 5, 20)));

        Assert.NotNull(issuedDebitNote.DebitNoteBillId);

        var debitNote = accountsPayableBillService.GetBill(issuedDebitNote.DebitNoteBillId!.Value);
        Assert.NotNull(debitNote);
        Assert.Equal("DEBIT_NOTE", debitNote!.BillType);
        Assert.Equal(createdReturn.PurchaseReturnId, debitNote.PurchaseReturnId);

        var submittedDebitNote = accountsPayableBillService.SubmitBill(debitNote.AccountsPayableBillId, 5);
        Assert.Equal("SUBMITTED", submittedDebitNote.StatusCode);

        var match = accountsPayableBillService.GetBillMatch(debitNote.AccountsPayableBillId);
        Assert.Equal(0, match.BlockingIssueCount);
        Assert.True(match.IsEligibleForApproval);

        var approved = accountsPayableBillService.ApproveMatch(debitNote.AccountsPayableBillId, 6);
        Assert.True(approved.IsEligibleForPosting);

        var postedDebitNote = accountsPayableBillService.PostBill(debitNote.AccountsPayableBillId, 6);
        Assert.Equal("POSTED", postedDebitNote.StatusCode);
        Assert.Equal("DEBIT_NOTE", postedDebitNote.BillType);
        Assert.NotNull(postedDebitNote.GeneralLedgerBatchId);

        var ledgerBatch = generalLedgerService.GetGeneralLedgerBatches()
            .Single(batch => batch.BatchType == "AP_DEBIT_NOTE" && batch.SourceDocumentId == debitNote.AccountsPayableBillId);
        Assert.NotEmpty(ledgerBatch.Journals);
    }
}
