using System.Net;
using System.Net.Http.Json;
using CashLineERP.Contracts.Finance;
using CashLineERP.Contracts.Sales;

namespace CashLineERP.Api.Tests;

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

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

    [Fact]
    public async Task Sales_Dispatch_Invoice_Receipt_Flow_Produces_Finance_Traceability()
    {
        await _client.AuthenticateAsFinanceMakerAsync();

        var createSalesOrderResponse = await _client.PostAsJsonAsync("/api/v1/sales/orders", new CreateSalesOrderRequest(
            BranchId: 1,
            CustomerId: 2,
            OrderDate: new DateOnly(2026, 4, 8),
            CurrencyCode: "EGP",
            ExchangeRate: 1m,
            Lines:
            [
                new CreateSalesOrderLineRequest(1001, 1, 8m, "EA", 32m, "VAT14"),
                new CreateSalesOrderLineRequest(1002, 1, 5m, "BOX", 49m, "VAT14")
            ]));

        Assert.True(createSalesOrderResponse.IsSuccessStatusCode, await createSalesOrderResponse.Content.ReadAsStringAsync());
        var salesOrder = await createSalesOrderResponse.Content.ReadFromJsonAsync<SalesOrderResponse>();
        Assert.NotNull(salesOrder);
        Assert.True(salesOrder!.SalesOrderId > 0, await createSalesOrderResponse.Content.ReadAsStringAsync());

        var getSalesOrderResponse = await _client.GetAsync($"/api/v1/sales/orders/{salesOrder.SalesOrderId}");
        Assert.True(getSalesOrderResponse.IsSuccessStatusCode, await getSalesOrderResponse.Content.ReadAsStringAsync());

        var submitSalesOrderResponse = await _client.PostAsync($"/api/v1/sales/orders/{salesOrder.SalesOrderId}/submit", null);
        Assert.True(submitSalesOrderResponse.IsSuccessStatusCode, await submitSalesOrderResponse.Content.ReadAsStringAsync());

        var createDispatchResponse = await _client.PostAsJsonAsync("/api/v1/sales/dispatches", new CreateDispatchOrderRequest(
            BranchId: 1,
            SalesOrderId: salesOrder.SalesOrderId,
            DispatchWarehouseId: 1,
            PlannedShipAtUtc: new DateTime(2026, 4, 8, 10, 0, 0, DateTimeKind.Utc),
            Lines:
            [
                new CreateDispatchOrderLineRequest(salesOrder.Lines.First().SalesOrderLineId, 1001, 8m, null, "BATCH-1001-B", null),
                new CreateDispatchOrderLineRequest(salesOrder.Lines.Last().SalesOrderLineId, 1002, 5m, null, "BATCH-1002-B", null)
            ]));

        Assert.True(createDispatchResponse.IsSuccessStatusCode, await createDispatchResponse.Content.ReadAsStringAsync());
        var dispatch = await createDispatchResponse.Content.ReadFromJsonAsync<DispatchOrderResponse>();
        Assert.NotNull(dispatch);

        var postDispatchResponse = await _client.PostAsJsonAsync($"/api/v1/sales/dispatches/{dispatch!.DispatchOrderId}/post", new PostDispatchOrderRequest(
            ActorUserId: 6,
            ShippedAtUtc: new DateTime(2026, 4, 8, 12, 0, 0, DateTimeKind.Utc),
            DeliveredAtUtc: new DateTime(2026, 4, 8, 15, 0, 0, DateTimeKind.Utc),
            ProofOfDeliveryReceiver: "Cairo Retail Receiver"));
        Assert.True(postDispatchResponse.IsSuccessStatusCode, await postDispatchResponse.Content.ReadAsStringAsync());

        var createInvoiceResponse = await _client.PostAsJsonAsync("/api/v1/finance/ar-invoices", new CreateAccountsReceivableInvoiceRequest(
            CustomerId: 2,
            SalesOrderId: salesOrder.SalesOrderId,
            DispatchOrderId: dispatch.DispatchOrderId,
            InvoiceType: "TAX",
            InvoiceDate: new DateOnly(2026, 4, 8),
            TaxDate: new DateOnly(2026, 4, 8),
            DueDate: new DateOnly(2026, 4, 20),
            CurrencyCode: "EGP",
            Lines:
            [
                new CreateAccountsReceivableInvoiceLineRequest(salesOrder.Lines.First().SalesOrderLineId, 1001, 8m, "EA", 32m, "VAT14"),
                new CreateAccountsReceivableInvoiceLineRequest(salesOrder.Lines.Last().SalesOrderLineId, 1002, 5m, "BOX", 49m, "VAT14")
            ]));

        Assert.True(createInvoiceResponse.IsSuccessStatusCode, await createInvoiceResponse.Content.ReadAsStringAsync());
        var invoice = await createInvoiceResponse.Content.ReadFromJsonAsync<AccountsReceivableInvoiceResponse>();
        Assert.NotNull(invoice);

        var submitInvoiceResponse = await _client.PostAsync($"/api/v1/finance/ar-invoices/{invoice!.AccountsReceivableInvoiceId}/submit", null);
        Assert.True(submitInvoiceResponse.IsSuccessStatusCode, await submitInvoiceResponse.Content.ReadAsStringAsync());

        await _client.AuthenticateAsFinanceCheckerAsync();

        var approveInvoiceResponse = await _client.PostAsync($"/api/v1/finance/ar-invoices/{invoice.AccountsReceivableInvoiceId}/approve", null);
        Assert.True(approveInvoiceResponse.IsSuccessStatusCode, await approveInvoiceResponse.Content.ReadAsStringAsync());

        var postInvoiceResponse = await _client.PostAsync($"/api/v1/finance/ar-invoices/{invoice.AccountsReceivableInvoiceId}/post", null);
        Assert.True(postInvoiceResponse.IsSuccessStatusCode, await postInvoiceResponse.Content.ReadAsStringAsync());

        var postedInvoice = await postInvoiceResponse.Content.ReadFromJsonAsync<AccountsReceivableInvoiceResponse>();
        Assert.NotNull(postedInvoice);
        Assert.Equal("POSTED", postedInvoice!.StatusCode);
        Assert.NotNull(postedInvoice.GeneralLedgerBatchId);

        await _client.AuthenticateAsFinanceMakerAsync();

        var createReceiptResponse = await _client.PostAsJsonAsync("/api/v1/finance/ar-receipts", new CreateAccountsReceivableReceiptRequest(
            CustomerId: 2,
            ReceiptDate: new DateOnly(2026, 4, 9),
            ReceiptMethod: "BANK_TRANSFER",
            CurrencyCode: "EGP",
            AmountAmount: postedInvoice.TotalAmount,
            ReferenceNumber: "BANK-AR-0002",
            Notes: "Customer paid full invoice",
            Allocations:
            [
                new CreateAccountsReceivableReceiptAllocationRequest(postedInvoice.AccountsReceivableInvoiceId, postedInvoice.TotalAmount, new DateOnly(2026, 4, 9))
            ]));

        Assert.True(createReceiptResponse.IsSuccessStatusCode, await createReceiptResponse.Content.ReadAsStringAsync());
        var receipt = await createReceiptResponse.Content.ReadFromJsonAsync<AccountsReceivableReceiptResponse>();
        Assert.NotNull(receipt);

        var submitReceiptResponse = await _client.PostAsync($"/api/v1/finance/ar-receipts/{receipt!.AccountsReceivableReceiptId}/submit", null);
        Assert.True(submitReceiptResponse.IsSuccessStatusCode, await submitReceiptResponse.Content.ReadAsStringAsync());

        await _client.AuthenticateAsFinanceCheckerAsync();

        var approveReceiptResponse = await _client.PostAsync($"/api/v1/finance/ar-receipts/{receipt.AccountsReceivableReceiptId}/approve", null);
        Assert.True(approveReceiptResponse.IsSuccessStatusCode, await approveReceiptResponse.Content.ReadAsStringAsync());

        var postReceiptResponse = await _client.PostAsync($"/api/v1/finance/ar-receipts/{receipt.AccountsReceivableReceiptId}/post", null);
        Assert.True(postReceiptResponse.IsSuccessStatusCode, await postReceiptResponse.Content.ReadAsStringAsync());

        var postedReceipt = await postReceiptResponse.Content.ReadFromJsonAsync<AccountsReceivableReceiptResponse>();
        Assert.NotNull(postedReceipt);
        Assert.Equal("POSTED", postedReceipt!.StatusCode);
        Assert.Equal(0m, postedReceipt.UnappliedAmount);
        Assert.NotNull(postedReceipt.GeneralLedgerBatchId);
    }

    [Fact]
    public async Task Customer_Receipt_Can_Be_Reversed_And_Reallocated()
    {
        await _client.AuthenticateAsFinanceMakerAsync();

        async Task<AccountsReceivableInvoiceResponse> CreatePostedInvoiceAsync(string referenceSuffix, DateOnly invoiceDate)
        {
            var createInvoiceResponse = await _client.PostAsJsonAsync("/api/v1/finance/ar-invoices", new CreateAccountsReceivableInvoiceRequest(
                CustomerId: 2,
                SalesOrderId: 1,
                DispatchOrderId: 1,
                InvoiceType: "TAX",
                InvoiceDate: invoiceDate,
                TaxDate: invoiceDate,
                DueDate: invoiceDate.AddDays(10),
                CurrencyCode: "EGP",
                Lines:
                [
                    new CreateAccountsReceivableInvoiceLineRequest(1, 1001, 1m, "EA", 32m, "VAT14")
                ]));

            Assert.True(createInvoiceResponse.IsSuccessStatusCode, await createInvoiceResponse.Content.ReadAsStringAsync());
            var invoice = await createInvoiceResponse.Content.ReadFromJsonAsync<AccountsReceivableInvoiceResponse>();
            Assert.NotNull(invoice);

            var submitInvoiceResponse = await _client.PostAsync($"/api/v1/finance/ar-invoices/{invoice!.AccountsReceivableInvoiceId}/submit", null);
            Assert.True(submitInvoiceResponse.IsSuccessStatusCode, await submitInvoiceResponse.Content.ReadAsStringAsync());

            await _client.AuthenticateAsFinanceCheckerAsync();

            var approveInvoiceResponse = await _client.PostAsync($"/api/v1/finance/ar-invoices/{invoice.AccountsReceivableInvoiceId}/approve", null);
            Assert.True(approveInvoiceResponse.IsSuccessStatusCode, await approveInvoiceResponse.Content.ReadAsStringAsync());

            var postInvoiceResponse = await _client.PostAsync($"/api/v1/finance/ar-invoices/{invoice.AccountsReceivableInvoiceId}/post", null);
            Assert.True(postInvoiceResponse.IsSuccessStatusCode, await postInvoiceResponse.Content.ReadAsStringAsync());

            var postedInvoice = await postInvoiceResponse.Content.ReadFromJsonAsync<AccountsReceivableInvoiceResponse>();
            Assert.NotNull(postedInvoice);
            await _client.AuthenticateAsFinanceMakerAsync();
            return postedInvoice!;
        }

        var postedInvoiceOne = await CreatePostedInvoiceAsync("A", new DateOnly(2026, 4, 16));
        var postedInvoiceTwo = await CreatePostedInvoiceAsync("B", new DateOnly(2026, 4, 17));

        var createReceiptResponse = await _client.PostAsJsonAsync("/api/v1/finance/ar-receipts", new CreateAccountsReceivableReceiptRequest(
            CustomerId: 2,
            ReceiptDate: new DateOnly(2026, 4, 18),
            ReceiptMethod: "BANK_TRANSFER",
            CurrencyCode: "EGP",
            AmountAmount: postedInvoiceOne.TotalAmount,
            ReferenceNumber: "BANK-AR-REV-0001",
            Notes: "Original receipt allocation",
            Allocations:
            [
                new CreateAccountsReceivableReceiptAllocationRequest(postedInvoiceOne.AccountsReceivableInvoiceId, postedInvoiceOne.TotalAmount, new DateOnly(2026, 4, 18))
            ]));
        Assert.True(createReceiptResponse.IsSuccessStatusCode, await createReceiptResponse.Content.ReadAsStringAsync());

        var receipt = await createReceiptResponse.Content.ReadFromJsonAsync<AccountsReceivableReceiptResponse>();
        Assert.NotNull(receipt);

        var submitReceiptResponse = await _client.PostAsync($"/api/v1/finance/ar-receipts/{receipt!.AccountsReceivableReceiptId}/submit", null);
        Assert.True(submitReceiptResponse.IsSuccessStatusCode, await submitReceiptResponse.Content.ReadAsStringAsync());

        await _client.AuthenticateAsFinanceCheckerAsync();

        var approveReceiptResponse = await _client.PostAsync($"/api/v1/finance/ar-receipts/{receipt.AccountsReceivableReceiptId}/approve", null);
        Assert.True(approveReceiptResponse.IsSuccessStatusCode, await approveReceiptResponse.Content.ReadAsStringAsync());

        var postReceiptResponse = await _client.PostAsync($"/api/v1/finance/ar-receipts/{receipt.AccountsReceivableReceiptId}/post", null);
        Assert.True(postReceiptResponse.IsSuccessStatusCode, await postReceiptResponse.Content.ReadAsStringAsync());

        var reverseReceiptResponse = await _client.PostAsJsonAsync(
            $"/api/v1/finance/ar-receipts/{receipt.AccountsReceivableReceiptId}/reverse",
            new ReverseAccountsReceivableReceiptRequest(
                ReversalDate: new DateOnly(2026, 4, 19),
                ReasonCode: "MISAPPLIED",
                Notes: "Receipt was initially applied to the wrong invoice."));
        Assert.True(reverseReceiptResponse.IsSuccessStatusCode, await reverseReceiptResponse.Content.ReadAsStringAsync());

        var reversedReceipt = await reverseReceiptResponse.Content.ReadFromJsonAsync<AccountsReceivableReceiptResponse>();
        Assert.NotNull(reversedReceipt);
        Assert.Equal("REVERSED", reversedReceipt!.StatusCode);

        var reallocateResponse = await _client.PostAsJsonAsync(
            $"/api/v1/finance/ar-receipts/{receipt.AccountsReceivableReceiptId}/reallocate",
            new ReallocateAccountsReceivableReceiptRequest(
                ReceiptDate: new DateOnly(2026, 4, 19),
                ReceiptMethod: "BANK_TRANSFER",
                ReferenceNumber: "BANK-AR-REV-0001-R1",
                Notes: "Replacement allocation after reversal.",
                Allocations:
                [
                    new CreateAccountsReceivableReceiptAllocationRequest(postedInvoiceTwo.AccountsReceivableInvoiceId, postedInvoiceTwo.TotalAmount, new DateOnly(2026, 4, 19))
                ]));
        Assert.True(reallocateResponse.IsSuccessStatusCode, await reallocateResponse.Content.ReadAsStringAsync());

        var replacementReceipt = await reallocateResponse.Content.ReadFromJsonAsync<AccountsReceivableReceiptResponse>();
        Assert.NotNull(replacementReceipt);
        Assert.Equal(receipt.AccountsReceivableReceiptId, replacementReceipt!.ReallocatedFromReceiptId);

        await _client.AuthenticateAsFinanceMakerAsync();
        var submitReplacementResponse = await _client.PostAsync($"/api/v1/finance/ar-receipts/{replacementReceipt.AccountsReceivableReceiptId}/submit", null);
        Assert.True(submitReplacementResponse.IsSuccessStatusCode, await submitReplacementResponse.Content.ReadAsStringAsync());

        await _client.AuthenticateAsFinanceCheckerAsync();
        var approveReplacementResponse = await _client.PostAsync($"/api/v1/finance/ar-receipts/{replacementReceipt.AccountsReceivableReceiptId}/approve", null);
        Assert.True(approveReplacementResponse.IsSuccessStatusCode, await approveReplacementResponse.Content.ReadAsStringAsync());

        var postReplacementResponse = await _client.PostAsync($"/api/v1/finance/ar-receipts/{replacementReceipt.AccountsReceivableReceiptId}/post", null);
        Assert.True(postReplacementResponse.IsSuccessStatusCode, await postReplacementResponse.Content.ReadAsStringAsync());

        var repostedReplacement = await postReplacementResponse.Content.ReadFromJsonAsync<AccountsReceivableReceiptResponse>();
        Assert.NotNull(repostedReplacement);
        Assert.Equal("POSTED", repostedReplacement!.StatusCode);
        Assert.NotNull(repostedReplacement.GeneralLedgerBatchId);
    }
}
