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

namespace CashLineERP.Api.Tests;

public sealed class ProcureToPayServiceQaTests
{
    [Fact]
    public void Accounts_Payable_Bill_Flow_Requires_Match_Before_Posting()
    {
        using var dbContext = QaDbContextFactory.CreateContext();
        var service = new AccountsPayableBillService(new AccountsPayableBillRepository(dbContext));

        var created = service.CreateBill(new CreateAccountsPayableBillRequest(
            SupplierId: 1,
            PurchaseOrderId: 1,
            GoodsReceiptId: 1,
            BillDate: new DateOnly(2026, 4, 8),
            DueDate: new DateOnly(2026, 4, 20),
            CurrencyCode: "EGP",
            Lines:
            [
                new CreateAccountsPayableBillLineRequest(1, 1001, 50m, "EA", 24m, "VAT14"),
                new CreateAccountsPayableBillLineRequest(2, 1002, 30m, "BOX", 50m, "VAT14")
            ]));

        var submitted = service.SubmitBill(created.AccountsPayableBillId, 3);
        Assert.Equal("SUBMITTED", submitted.StatusCode);

        var prematurePost = Record.Exception(() => service.PostBill(created.AccountsPayableBillId, 4));
        Assert.NotNull(prematurePost);
        Assert.IsType<InvalidOperationException>(prematurePost);

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

        var approved = service.ApproveMatch(created.AccountsPayableBillId, 4);
        Assert.True(approved.IsEligibleForPosting);
        Assert.Equal("MATCHED", approved.ThreeWayMatchStatus);

        var posted = service.PostBill(created.AccountsPayableBillId, 4);
        Assert.Equal("POSTED", posted.StatusCode);
        Assert.Equal("MATCHED", posted.ThreeWayMatchStatus);
        Assert.NotNull(posted.GeneralLedgerBatchId);
    }

    [Fact]
    public void Supplier_Invoice_Dispute_Hold_Blocks_Approval_And_Posting_Until_Released()
    {
        using var dbContext = QaDbContextFactory.CreateContext();
        var service = new AccountsPayableBillService(new AccountsPayableBillRepository(dbContext));

        var created = service.CreateBill(new CreateAccountsPayableBillRequest(
            SupplierId: 1,
            PurchaseOrderId: 1,
            GoodsReceiptId: 1,
            BillDate: new DateOnly(2026, 4, 9),
            DueDate: new DateOnly(2026, 4, 25),
            CurrencyCode: "EGP",
            Lines:
            [
                new CreateAccountsPayableBillLineRequest(1, 1001, 50m, "EA", 24m, "VAT14"),
                new CreateAccountsPayableBillLineRequest(2, 1002, 30m, "BOX", 50m, "VAT14")
            ]));

        var submitted = service.SubmitBill(created.AccountsPayableBillId, 3);
        Assert.Equal("SUBMITTED", submitted.StatusCode);

        var held = service.PlaceBillOnHold(created.AccountsPayableBillId, 4, new PlaceAccountsPayableBillOnHoldRequest(
            ReasonCode: "PRICE_VARIANCE",
            Notes: "Supplier price dispute under finance review."));

        Assert.Equal("ON_HOLD", held.DisputeStatusCode);
        Assert.Equal("PRICE_VARIANCE", held.DisputeReasonCode);

        var heldMatch = service.GetBillMatch(created.AccountsPayableBillId);
        Assert.False(heldMatch.IsEligibleForApproval);
        Assert.Contains("dispute hold is active", heldMatch.Summary, StringComparison.OrdinalIgnoreCase);

        var approvalBlocked = Record.Exception(() => service.ApproveMatch(created.AccountsPayableBillId, 4));
        Assert.NotNull(approvalBlocked);
        Assert.IsType<InvalidOperationException>(approvalBlocked);

        var resolved = service.ResolveBillDispute(created.AccountsPayableBillId, 4, new ResolveAccountsPayableBillDisputeRequest(
            DecisionCode: "RELEASE",
            Notes: "Variance accepted after supplier clarification."));

        Assert.Equal("RESOLVED", resolved.DisputeStatusCode);
        Assert.Equal("RELEASE", resolved.ResolutionDecisionCode);

        var approved = service.ApproveMatch(created.AccountsPayableBillId, 4);
        Assert.True(approved.IsEligibleForPosting);

        var posted = service.PostBill(created.AccountsPayableBillId, 4);
        Assert.Equal("POSTED", posted.StatusCode);
        Assert.Equal("RESOLVED", posted.DisputeStatusCode);
        Assert.NotNull(posted.GeneralLedgerBatchId);
    }
}
