using System.Net;
using System.Net.Http.Json;
using CashLineERP.Contracts.Procurement;

namespace CashLineERP.Api.Tests;

public sealed class ProcureToPayQaTests : IClassFixture<ApiTestFactory>
{
    private readonly HttpClient _client;

    public ProcureToPayQaTests(ApiTestFactory factory)
    {
        _client = factory.CreateClient();
    }

    [Fact]
    public async Task Accounts_Payable_Bill_Must_Be_Matched_Before_Posting()
    {
        await _client.AuthenticateAsInternalAsync();

        var createResponse = await _client.PostAsJsonAsync("/api/v1/procurement/ap-bills", 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 created = await createResponse.Content.ReadFromJsonAsync<AccountsPayableBillResponse>();
        Assert.NotNull(created);
        Assert.True(created!.AccountsPayableBillId > 0, await createResponse.Content.ReadAsStringAsync());

        var getCreatedResponse = await _client.GetAsync($"/api/v1/procurement/ap-bills/{created.AccountsPayableBillId}");
        Assert.True(getCreatedResponse.IsSuccessStatusCode, await getCreatedResponse.Content.ReadAsStringAsync());

        var submitResponse = await _client.PostAsync($"/api/v1/procurement/ap-bills/{created.AccountsPayableBillId}/submit", null);
        Assert.True(submitResponse.IsSuccessStatusCode, await submitResponse.Content.ReadAsStringAsync());

        var prematurePostResponse = await _client.PostAsync($"/api/v1/procurement/ap-bills/{created.AccountsPayableBillId}/post", null);
        Assert.Equal(HttpStatusCode.BadRequest, prematurePostResponse.StatusCode);

        var matchResponse = await _client.GetAsync($"/api/v1/procurement/ap-bills/{created.AccountsPayableBillId}/match");
        Assert.True(matchResponse.IsSuccessStatusCode, await matchResponse.Content.ReadAsStringAsync());

        var match = await matchResponse.Content.ReadFromJsonAsync<AccountsPayableBillMatchResponse>();
        Assert.NotNull(match);
        Assert.Equal(0, match!.BlockingIssueCount);
        Assert.True(match.IsEligibleForApproval);

        await _client.AuthenticateAsFinanceCheckerAsync();

        var approveResponse = await _client.PostAsync($"/api/v1/procurement/ap-bills/{created.AccountsPayableBillId}/approve-match", null);
        Assert.True(approveResponse.IsSuccessStatusCode, await approveResponse.Content.ReadAsStringAsync());

        var postResponse = await _client.PostAsync($"/api/v1/procurement/ap-bills/{created.AccountsPayableBillId}/post", null);
        Assert.True(postResponse.IsSuccessStatusCode, await postResponse.Content.ReadAsStringAsync());

        var posted = await postResponse.Content.ReadFromJsonAsync<AccountsPayableBillResponse>();
        Assert.NotNull(posted);
        Assert.Equal("POSTED", posted!.StatusCode);
        Assert.Equal("MATCHED", posted.ThreeWayMatchStatus);
        Assert.NotNull(posted.GeneralLedgerBatchId);
    }

    [Fact]
    public async Task Supplier_Invoice_Dispute_Hold_Blocks_Posting_Until_Released()
    {
        await _client.AuthenticateAsInternalAsync();

        var createResponse = await _client.PostAsJsonAsync("/api/v1/procurement/ap-bills", new CreateAccountsPayableBillRequest(
            SupplierId: 1,
            PurchaseOrderId: 1,
            GoodsReceiptId: 1,
            BillDate: new DateOnly(2026, 4, 10),
            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 created = await createResponse.Content.ReadFromJsonAsync<AccountsPayableBillResponse>();
        Assert.NotNull(created);

        var submitResponse = await _client.PostAsync($"/api/v1/procurement/ap-bills/{created!.AccountsPayableBillId}/submit", null);
        Assert.True(submitResponse.IsSuccessStatusCode, await submitResponse.Content.ReadAsStringAsync());

        await _client.AuthenticateAsFinanceCheckerAsync();

        var holdResponse = await _client.PostAsJsonAsync(
            $"/api/v1/procurement/ap-bills/{created.AccountsPayableBillId}/hold",
            new PlaceAccountsPayableBillOnHoldRequest("PRICE_VARIANCE", "Supplier amount disputed by finance."));
        Assert.True(holdResponse.IsSuccessStatusCode, await holdResponse.Content.ReadAsStringAsync());

        var matchWhileHeldResponse = await _client.GetAsync($"/api/v1/procurement/ap-bills/{created.AccountsPayableBillId}/match");
        Assert.True(matchWhileHeldResponse.IsSuccessStatusCode, await matchWhileHeldResponse.Content.ReadAsStringAsync());

        var heldMatch = await matchWhileHeldResponse.Content.ReadFromJsonAsync<AccountsPayableBillMatchResponse>();
        Assert.NotNull(heldMatch);
        Assert.False(heldMatch!.IsEligibleForApproval);

        var approveWhileHeldResponse = await _client.PostAsync($"/api/v1/procurement/ap-bills/{created.AccountsPayableBillId}/approve-match", null);
        Assert.Equal(HttpStatusCode.BadRequest, approveWhileHeldResponse.StatusCode);

        var resolveResponse = await _client.PostAsJsonAsync(
            $"/api/v1/procurement/ap-bills/{created.AccountsPayableBillId}/resolve-dispute",
            new ResolveAccountsPayableBillDisputeRequest("RELEASE", "Supplier clarification accepted."));
        Assert.True(resolveResponse.IsSuccessStatusCode, await resolveResponse.Content.ReadAsStringAsync());

        var approveResponse = await _client.PostAsync($"/api/v1/procurement/ap-bills/{created.AccountsPayableBillId}/approve-match", null);
        Assert.True(approveResponse.IsSuccessStatusCode, await approveResponse.Content.ReadAsStringAsync());

        var postResponse = await _client.PostAsync($"/api/v1/procurement/ap-bills/{created.AccountsPayableBillId}/post", null);
        Assert.True(postResponse.IsSuccessStatusCode, await postResponse.Content.ReadAsStringAsync());

        var posted = await postResponse.Content.ReadFromJsonAsync<AccountsPayableBillResponse>();
        Assert.NotNull(posted);
        Assert.Equal("POSTED", posted!.StatusCode);
        Assert.Equal("RESOLVED", posted.DisputeStatusCode);
        Assert.Equal("RELEASE", posted.ResolutionDecisionCode);
    }
}
