using System.Net;
using System.Net.Http.Json;
using System.Text;
using CashLineERP.Contracts.Finance;
using CashLineERP.Contracts.Integration;
using CashLineERP.Contracts.Procurement;
using CashLineERP.SharedKernel.Finance;

namespace CashLineERP.Api.Tests;

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

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

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

        var outbound = await CreateAndActivateEndpointAsync("HTTP Paymob refund outbound", "PAYMOB", "OUTBOUND", "/refunds");
        var inbound = await CreateAndActivateEndpointAsync("HTTP Paymob refund callback", "PAYMOB", "INBOUND", "/refund-callback");
        var channel = await CreateAndActivateChannelAsync(outbound, inbound);
        var postedRefund = await CreatePostedRefundAsync(channel.PaymentChannelConfigurationId, "HTTP-PAYEXEC-0001", 75m);

        var queueResponse = await _client.PostAsync($"/api/v1/finance/customer-refunds/{postedRefund.CustomerRefundId}/queue-provider-execution", null);
        Assert.Equal(HttpStatusCode.OK, queueResponse.StatusCode);
        var queued = await queueResponse.Content.ReadFromJsonAsync<PaymentGatewayExecutionResponse>();
        Assert.NotNull(queued);
        Assert.Equal("QUEUED", queued!.StatusCode);

        var dispatchResponse = await _client.PostAsJsonAsync("/api/v1/finance/payment-executions/dispatch-batch", new DispatchPaymentGatewayExecutionBatchRequest(
            ProcessorCode: "HTTP_PAYMENT_EXEC_WORKER",
            BatchSize: 10,
            PaymentChannelConfigurationId: channel.PaymentChannelConfigurationId));
        Assert.Equal(HttpStatusCode.OK, dispatchResponse.StatusCode);
        var dispatched = await dispatchResponse.Content.ReadFromJsonAsync<DispatchPaymentGatewayExecutionBatchResultResponse>();
        Assert.NotNull(dispatched);
        Assert.Single(dispatched!.Executions);
        Assert.Equal("DISPATCHED", dispatched.Executions.Single().StatusCode);

        _client.DefaultRequestHeaders.Authorization = null;

        var payloadText = $$"""
        {
          "correlationKey": "{{queued.CorrelationKey}}",
          "providerReference": "{{dispatched.Executions.Single().ProviderReference}}",
          "settlementStatus": "SETTLED",
          "settlementDate": "2026-05-20",
          "settledAmount": 75.00,
          "feeAmount": 1.50,
          "netAmount": 73.50,
          "externalBatchReference": "HTTP-SETTLE-001"
        }
        """;
        var signature = GatewayCallbackSecurity.ComputeSignature("secret://paymob/uat/key", payloadText, "HTTP-CB-0001", queued.CorrelationKey);

        using var payload = new StringContent(payloadText, Encoding.UTF8, "application/json");
        payload.Headers.Add("X-Callback-Signature", signature);
        payload.Headers.Add("X-External-Message-Id", "HTTP-CB-0001");
        payload.Headers.Add("X-Correlation-Key", queued.CorrelationKey);
        payload.Headers.Add("X-Message-Type", "SETTLEMENT_STATUS");

        var callbackResponse = await _client.PostAsync($"/api/v1/payments/channels/{channel.PaymentChannelConfigurationId}/callback", payload);
        Assert.Equal(HttpStatusCode.OK, callbackResponse.StatusCode);

        await _client.AuthenticateAsFinanceMakerAsync();

        var processResponse = await _client.PostAsJsonAsync($"/api/v1/finance/payment-channels/{channel.PaymentChannelConfigurationId}/callbacks/process-batch", new ProcessGatewayCallbackBatchRequest(
            ProcessorCode: "HTTP_CALLBACK_WORKER",
            BatchSize: 10));
        Assert.Equal(HttpStatusCode.OK, processResponse.StatusCode);
        var processed = await processResponse.Content.ReadFromJsonAsync<ProcessGatewayCallbackBatchResultResponse>();
        Assert.NotNull(processed);
        Assert.Equal(1, processed!.ProcessedCount);
        Assert.Equal(1, processed.UpdatedExecutionCount);

        var executionsResponse = await _client.GetAsync($"/api/v1/finance/customer-refunds/{postedRefund.CustomerRefundId}/provider-executions");
        Assert.Equal(HttpStatusCode.OK, executionsResponse.StatusCode);
        var executions = await executionsResponse.Content.ReadFromJsonAsync<List<PaymentGatewayExecutionResponse>>();
        Assert.NotNull(executions);
        Assert.Contains(executions!, item => item.StatusCode == "SETTLED");
    }

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

        var outbound = await CreateAndActivateEndpointAsync("HTTP Paymob collection outbound", "PAYMOB", "OUTBOUND", "/checkout");
        var inbound = await CreateAndActivateEndpointAsync("HTTP Paymob collection callback", "PAYMOB", "INBOUND", "/checkout-callback");
        var channel = await CreateAndActivateChannelAsync(
            outbound,
            inbound,
            usageCode: "CUSTOMER_COLLECTION",
            channelName: "HTTP Paymob collection channel",
            notes: "HTTP collection execution channel");

        var invoice = await CreatePostedInvoiceAsync("HTTP-PAYCOL-0001", 96m);

        await _client.AuthenticateAsCustomerAsync();

        var createPaymentResponse = await _client.PostAsJsonAsync("/api/v1/external/parties/2/payments", new CashLineERP.Contracts.External.CreateExternalCustomerPaymentRequest(
            AccountsReceivableInvoiceId: invoice.AccountsReceivableInvoiceId,
            IntegrationProviderCode: "PAYMOB",
            CurrencyCode: "EGP",
            AmountAmount: invoice.TotalAmount,
            PaymentDate: new DateOnly(2026, 5, 20),
            ReferenceNumber: "HTTP-PAYCOL-0001",
            Notes: "Customer collection request from the external payment QA path."));
        Assert.Equal(HttpStatusCode.OK, createPaymentResponse.StatusCode);
        var createdPayment = await createPaymentResponse.Content.ReadFromJsonAsync<CashLineERP.Contracts.External.ExternalPaymentResponse>();
        Assert.NotNull(createdPayment);
        Assert.Equal("CUSTOMER_PAYMENT_REQUEST", createdPayment!.PaymentType);
        Assert.Equal("PENDING_CUSTOMER_ACTION", createdPayment.StatusCode);

        await _client.AuthenticateAsFinanceMakerAsync();

        var dispatchResponse = await _client.PostAsJsonAsync("/api/v1/finance/payment-executions/dispatch-batch", new DispatchPaymentGatewayExecutionBatchRequest(
            ProcessorCode: "HTTP_PAYMENT_EXEC_WORKER",
            BatchSize: 10,
            PaymentChannelConfigurationId: channel.PaymentChannelConfigurationId));
        Assert.Equal(HttpStatusCode.OK, dispatchResponse.StatusCode);
        var dispatched = await dispatchResponse.Content.ReadFromJsonAsync<DispatchPaymentGatewayExecutionBatchResultResponse>();
        Assert.NotNull(dispatched);
        Assert.Single(dispatched!.Executions);
        var dispatchedExecution = dispatched.Executions.Single();
        Assert.Equal("CUSTOMER_PAYMENT_REQUEST", dispatchedExecution.SourceDocumentType);

        _client.DefaultRequestHeaders.Authorization = null;

        var payloadText = $$"""
        {
          "correlationKey": "{{dispatchedExecution.CorrelationKey}}",
          "providerReference": "{{dispatchedExecution.ProviderReference}}",
          "settlementStatus": "SETTLED",
          "settledAmount": {{invoice.TotalAmount}},
          "notes": "Collection settled successfully."
        }
        """;
        var signature = GatewayCallbackSecurity.ComputeSignature("secret://paymob/uat/key", payloadText, "HTTP-COL-CB-0001", dispatchedExecution.CorrelationKey);

        using var payload = new StringContent(payloadText, Encoding.UTF8, "application/json");
        payload.Headers.Add("X-Callback-Signature", signature);
        payload.Headers.Add("X-External-Message-Id", "HTTP-COL-CB-0001");
        payload.Headers.Add("X-Correlation-Key", dispatchedExecution.CorrelationKey);
        payload.Headers.Add("X-Message-Type", "SETTLEMENT_STATUS");

        var callbackResponse = await _client.PostAsync($"/api/v1/payments/channels/{channel.PaymentChannelConfigurationId}/callback", payload);
        Assert.Equal(HttpStatusCode.OK, callbackResponse.StatusCode);

        await _client.AuthenticateAsFinanceMakerAsync();

        var processResponse = await _client.PostAsJsonAsync($"/api/v1/finance/payment-channels/{channel.PaymentChannelConfigurationId}/callbacks/process-batch", new ProcessGatewayCallbackBatchRequest(
            ProcessorCode: "HTTP_CALLBACK_WORKER",
            BatchSize: 10));
        Assert.Equal(HttpStatusCode.OK, processResponse.StatusCode);
        var processed = await processResponse.Content.ReadFromJsonAsync<ProcessGatewayCallbackBatchResultResponse>();
        Assert.NotNull(processed);
        Assert.Equal(1, processed!.ProcessedCount);
        Assert.Equal(1, processed.UpdatedExecutionCount);
        Assert.Equal(0, processed.SettlementMutationCount);

        await _client.AuthenticateAsCustomerAsync();

        var paymentsResponse = await _client.GetAsync("/api/v1/external/parties/2/payments");
        Assert.Equal(HttpStatusCode.OK, paymentsResponse.StatusCode);
        var payments = await paymentsResponse.Content.ReadFromJsonAsync<List<CashLineERP.Contracts.External.ExternalPaymentResponse>>();
        Assert.NotNull(payments);
        Assert.Contains(payments!, item => item.PaymentType == "CUSTOMER_PAYMENT_REQUEST" && item.PaymentNumber == createdPayment.PaymentNumber && item.StatusCode == "PAID");
        Assert.Contains(payments!, item => item.PaymentType == "AR_RECEIPT" && item.Amount == invoice.TotalAmount);
    }

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

        var outbound = await CreateAndActivateEndpointAsync("HTTP Fawry collection outbound", "FAWRY", "OUTBOUND", "/collect");
        var inbound = await CreateAndActivateEndpointAsync("HTTP Fawry collection callback", "FAWRY", "INBOUND", "/collect-callback");
        var channel = await CreateAndActivateChannelAsync(
            outbound,
            inbound,
            providerCode: "FAWRY",
            usageCode: "CUSTOMER_COLLECTION",
            channelName: "HTTP Fawry collection channel",
            notes: "HTTP collection execution channel");

        var invoice = await CreatePostedInvoiceAsync("HTTP-FAWRY-PAY-0001", 88m);

        await _client.AuthenticateAsCustomerAsync();

        var createPaymentResponse = await _client.PostAsJsonAsync("/api/v1/external/parties/2/payments", new CashLineERP.Contracts.External.CreateExternalCustomerPaymentRequest(
            AccountsReceivableInvoiceId: invoice.AccountsReceivableInvoiceId,
            IntegrationProviderCode: "FAWRY",
            CurrencyCode: "EGP",
            AmountAmount: invoice.TotalAmount,
            PaymentDate: new DateOnly(2026, 5, 21),
            ReferenceNumber: "HTTP-FAWRY-PAY-0001",
            Notes: "Customer Fawry payment request from the external payment QA path."));
        Assert.Equal(HttpStatusCode.OK, createPaymentResponse.StatusCode);
        var createdPayment = await createPaymentResponse.Content.ReadFromJsonAsync<CashLineERP.Contracts.External.ExternalPaymentResponse>();
        Assert.NotNull(createdPayment);

        var statusResponse = await _client.GetAsync($"/api/v1/external/parties/2/payments/{createdPayment!.PaymentId}/status");
        Assert.Equal(HttpStatusCode.OK, statusResponse.StatusCode);
        var status = await statusResponse.Content.ReadFromJsonAsync<CashLineERP.Contracts.External.ExternalCustomerPaymentStatusResponse>();
        Assert.NotNull(status);
        Assert.Equal("FAWRY", status!.IntegrationProviderCode);
        Assert.Equal("FAWRY_CHARGE_REQUEST", status.ProviderContract.ContractTypeCode);
        Assert.Contains("\"merchantRefNum\"", status.ProviderContract.RequestBodyJson);
        Assert.NotNull(status.ProviderContract.CallbackUrl);
        Assert.Contains($"/api/v1/payments/channels/{channel.PaymentChannelConfigurationId}/callback", status.ProviderContract.CallbackUrl);
    }

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

        var outbound = await CreateAndActivateEndpointAsync("HTTP Paymob finance collection outbound", "PAYMOB", "OUTBOUND", "/finance-checkout");
        var inbound = await CreateAndActivateEndpointAsync("HTTP Paymob finance collection callback", "PAYMOB", "INBOUND", "/finance-checkout-callback");
        var channel = await CreateAndActivateChannelAsync(
            outbound,
            inbound,
            providerCode: "PAYMOB",
            usageCode: "CUSTOMER_COLLECTION",
            channelName: "HTTP Paymob finance collection channel",
            notes: "HTTP finance-side customer collection channel");

        var invoice = await CreatePostedInvoiceAsync("HTTP-FIN-CPAY-0001", 61m);

        await _client.AuthenticateAsCustomerAsync();

        var createPaymentResponse = await _client.PostAsJsonAsync("/api/v1/external/parties/2/payments", new CashLineERP.Contracts.External.CreateExternalCustomerPaymentRequest(
            AccountsReceivableInvoiceId: invoice.AccountsReceivableInvoiceId,
            IntegrationProviderCode: "PAYMOB",
            CurrencyCode: "EGP",
            AmountAmount: invoice.TotalAmount,
            PaymentDate: new DateOnly(2026, 5, 22),
            ReferenceNumber: "HTTP-FIN-CPAY-0001",
            Notes: "Finance-side customer payment route QA."));
        Assert.Equal(HttpStatusCode.OK, createPaymentResponse.StatusCode);
        var createdPayment = await createPaymentResponse.Content.ReadFromJsonAsync<CashLineERP.Contracts.External.ExternalPaymentResponse>();
        Assert.NotNull(createdPayment);

        await _client.AuthenticateAsFinanceMakerAsync();

        var executionsResponse = await _client.GetAsync($"/api/v1/finance/customer-payments/{createdPayment!.PaymentId}/provider-executions");
        Assert.Equal(HttpStatusCode.OK, executionsResponse.StatusCode);
        var executions = await executionsResponse.Content.ReadFromJsonAsync<List<PaymentGatewayExecutionResponse>>();
        Assert.NotNull(executions);
        Assert.Single(executions!);
        Assert.Equal("CUSTOMER_PAYMENT_REQUEST", executions.Single().SourceDocumentType);

        var contractResponse = await _client.GetAsync($"/api/v1/finance/customer-payments/{createdPayment.PaymentId}/provider-contract");
        Assert.Equal(HttpStatusCode.OK, contractResponse.StatusCode);
        var contract = await contractResponse.Content.ReadFromJsonAsync<PaymentGatewayProviderContractResponse>();
        Assert.NotNull(contract);
        Assert.Equal("PAYMOB_CHECKOUT_INTENTION", contract!.ContractTypeCode);
        Assert.Contains("\"merchantOrderId\"", contract.RequestBodyJson);
        Assert.NotNull(contract.CallbackUrl);
        Assert.Contains($"/api/v1/payments/channels/{channel.PaymentChannelConfigurationId}/callback", contract.CallbackUrl);
    }

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

        var outbound = await CreateAndActivateEndpointAsync("HTTP Fawry payout outbound", "FAWRY", "OUTBOUND", "/supplier-payouts");
        var inbound = await CreateAndActivateEndpointAsync("HTTP Fawry payout callback", "FAWRY", "INBOUND", "/supplier-payout-callback");
        var channel = await CreateAndActivateChannelAsync(
            outbound,
            inbound,
            providerCode: "FAWRY",
            usageCode: "SUPPLIER_PAYOUT",
            channelName: "HTTP Fawry supplier payout channel",
            notes: "HTTP supplier payout execution channel");
        var postedPayment = await CreatePostedAccountsPayablePaymentAsync("HTTP-SUPP-PAYOUT-0001", 24m);

        var contractResponse = await _client.GetAsync($"/api/v1/finance/ap-payments/{postedPayment.AccountsPayablePaymentId}/provider-contract?integrationProviderCode=FAWRY");
        Assert.Equal(HttpStatusCode.OK, contractResponse.StatusCode);
        var contract = await contractResponse.Content.ReadFromJsonAsync<PaymentGatewayProviderContractResponse>();
        Assert.NotNull(contract);
        Assert.Equal("FAWRY_PAYOUT_REQUEST", contract!.ContractTypeCode);
        Assert.Contains("\"supplierProfileId\"", contract.RequestBodyJson);
        Assert.NotNull(contract.CallbackUrl);
        Assert.Contains($"/api/v1/payments/channels/{channel.PaymentChannelConfigurationId}/callback", contract.CallbackUrl);

        var queueResponse = await _client.PostAsJsonAsync(
            $"/api/v1/finance/ap-payments/{postedPayment.AccountsPayablePaymentId}/queue-provider-execution",
            new QueueSupplierPayoutExecutionRequest("FAWRY"));
        Assert.Equal(HttpStatusCode.OK, queueResponse.StatusCode);
        var queued = await queueResponse.Content.ReadFromJsonAsync<PaymentGatewayExecutionResponse>();
        Assert.NotNull(queued);
        Assert.Equal("AP_PAYMENT", queued!.SourceDocumentType);
        Assert.Equal("QUEUED", queued.StatusCode);

        var dispatchResponse = await _client.PostAsJsonAsync("/api/v1/finance/payment-executions/dispatch-batch", new DispatchPaymentGatewayExecutionBatchRequest(
            ProcessorCode: "HTTP_PAYMENT_EXEC_WORKER",
            BatchSize: 10,
            PaymentChannelConfigurationId: channel.PaymentChannelConfigurationId));
        Assert.Equal(HttpStatusCode.OK, dispatchResponse.StatusCode);
        var dispatched = await dispatchResponse.Content.ReadFromJsonAsync<DispatchPaymentGatewayExecutionBatchResultResponse>();
        Assert.NotNull(dispatched);
        Assert.Single(dispatched!.Executions);
        Assert.Equal("DISPATCHED", dispatched.Executions.Single().StatusCode);

        _client.DefaultRequestHeaders.Authorization = null;

        var payloadText = $$"""
        {
          "correlationKey": "{{queued.CorrelationKey}}",
          "providerReference": "{{dispatched.Executions.Single().ProviderReference}}",
          "settlementStatus": "SETTLED",
          "settlementDate": "2026-05-22",
          "settledAmount": {{postedPayment.AmountAmount}},
          "feeAmount": 1.25,
          "netAmount": {{postedPayment.AmountAmount - 1.25m}},
          "externalBatchReference": "HTTP-SUPP-SETTLE-001"
        }
        """;
        var signature = GatewayCallbackSecurity.ComputeSignature("secret://fawry/uat/key", payloadText, "HTTP-SUPP-CB-0001", queued.CorrelationKey);

        using var payload = new StringContent(payloadText, Encoding.UTF8, "application/json");
        payload.Headers.Add("X-Callback-Signature", signature);
        payload.Headers.Add("X-External-Message-Id", "HTTP-SUPP-CB-0001");
        payload.Headers.Add("X-Correlation-Key", queued.CorrelationKey);
        payload.Headers.Add("X-Message-Type", "SETTLEMENT_STATUS");

        var callbackResponse = await _client.PostAsync($"/api/v1/payments/channels/{channel.PaymentChannelConfigurationId}/callback", payload);
        Assert.Equal(HttpStatusCode.OK, callbackResponse.StatusCode);

        await _client.AuthenticateAsFinanceMakerAsync();

        var processResponse = await _client.PostAsJsonAsync($"/api/v1/finance/payment-channels/{channel.PaymentChannelConfigurationId}/callbacks/process-batch", new ProcessGatewayCallbackBatchRequest(
            ProcessorCode: "HTTP_CALLBACK_WORKER",
            BatchSize: 10));
        Assert.Equal(HttpStatusCode.OK, processResponse.StatusCode);
        var processed = await processResponse.Content.ReadFromJsonAsync<ProcessGatewayCallbackBatchResultResponse>();
        Assert.NotNull(processed);
        Assert.Equal(1, processed!.ProcessedCount);
        Assert.Equal(1, processed.UpdatedExecutionCount);
        Assert.Equal(1, processed.SettlementMutationCount);

        var executionsResponse = await _client.GetAsync($"/api/v1/finance/ap-payments/{postedPayment.AccountsPayablePaymentId}/provider-executions");
        Assert.Equal(HttpStatusCode.OK, executionsResponse.StatusCode);
        var executions = await executionsResponse.Content.ReadFromJsonAsync<List<PaymentGatewayExecutionResponse>>();
        Assert.NotNull(executions);
        Assert.Contains(executions!, item => item.SourceDocumentType == "AP_PAYMENT" && item.StatusCode == "SETTLED");
    }

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

        var outbound = await CreateAndActivateEndpointAsync("HTTP Fawry server API outbound", "FAWRY", "OUTBOUND", "/collect");
        var inbound = await CreateAndActivateEndpointAsync("HTTP Fawry server API callback", "FAWRY", "INBOUND", "/collect-callback");
        var channel = await CreateAndActivateChannelAsync(
            outbound,
            inbound,
            providerCode: "FAWRY",
            usageCode: "CUSTOMER_COLLECTION",
            channelName: "HTTP Fawry server API collection channel",
            notes: "HTTP Fawry collection channel for server API operation contract QA");

        var invoice = await CreatePostedInvoiceAsync("HTTP-FAWRY-SERVER-OPS-0001", 74m);

        await _client.AuthenticateAsCustomerAsync();

        var createPaymentResponse = await _client.PostAsJsonAsync("/api/v1/external/parties/2/payments", new CashLineERP.Contracts.External.CreateExternalCustomerPaymentRequest(
            AccountsReceivableInvoiceId: invoice.AccountsReceivableInvoiceId,
            IntegrationProviderCode: "FAWRY",
            CurrencyCode: "EGP",
            AmountAmount: invoice.TotalAmount,
            PaymentDate: new DateOnly(2026, 5, 23),
            ReferenceNumber: "HTTP-FAWRY-SERVER-OPS-0001",
            Notes: "Customer Fawry payment request for server API operation contract QA."));
        Assert.Equal(HttpStatusCode.OK, createPaymentResponse.StatusCode);
        var createdPayment = await createPaymentResponse.Content.ReadFromJsonAsync<CashLineERP.Contracts.External.ExternalPaymentResponse>();
        Assert.NotNull(createdPayment);

        await _client.AuthenticateAsFinanceMakerAsync();

        var executionsResponse = await _client.GetAsync($"/api/v1/finance/customer-payments/{createdPayment!.PaymentId}/provider-executions");
        Assert.Equal(HttpStatusCode.OK, executionsResponse.StatusCode);
        var executions = await executionsResponse.Content.ReadFromJsonAsync<List<PaymentGatewayExecutionResponse>>();
        Assert.NotNull(executions);
        Assert.Single(executions!);
        var queuedExecution = executions.Single();

        var dispatchResponse = await _client.PostAsJsonAsync("/api/v1/finance/payment-executions/dispatch-batch", new DispatchPaymentGatewayExecutionBatchRequest(
            ProcessorCode: "HTTP_PAYMENT_EXEC_WORKER",
            BatchSize: 10,
            PaymentChannelConfigurationId: channel.PaymentChannelConfigurationId));
        Assert.Equal(HttpStatusCode.OK, dispatchResponse.StatusCode);
        var dispatched = await dispatchResponse.Content.ReadFromJsonAsync<DispatchPaymentGatewayExecutionBatchResultResponse>();
        Assert.NotNull(dispatched);
        var dispatchedExecution = Assert.Single(dispatched!.Executions);
        Assert.Equal(queuedExecution.PaymentGatewayExecutionId, dispatchedExecution.PaymentGatewayExecutionId);
        Assert.Equal("DISPATCHED", dispatchedExecution.StatusCode);
        Assert.False(string.IsNullOrWhiteSpace(dispatchedExecution.ProviderReference));

        var statusContractResponse = await _client.GetAsync($"/api/v1/finance/payment-executions/{dispatchedExecution.PaymentGatewayExecutionId}/provider-status-contract");
        Assert.Equal(HttpStatusCode.OK, statusContractResponse.StatusCode);
        var statusContract = await statusContractResponse.Content.ReadFromJsonAsync<PaymentGatewayProviderContractResponse>();
        Assert.NotNull(statusContract);
        Assert.Equal("FAWRY_PAYMENT_STATUS_V2", statusContract!.ContractTypeCode);
        Assert.Contains("/ECommerceWeb/Fawry/payments/status/v2", statusContract.RequestUrl);
        Assert.Contains("\"merchantRefNumber\"", statusContract.RequestBodyJson);
        Assert.Contains(dispatchedExecution.SourceDocumentNumber, statusContract.RequestBodyJson);
        Assert.Contains("\"signatureRecipe\"", statusContract.RequestBodyJson);
        Assert.Contains("merchantCode", statusContract.RequestBodyJson);
        Assert.Contains("merchantRefNumber", statusContract.RequestBodyJson);
        Assert.Contains("secureKey", statusContract.RequestBodyJson);

        var cancelContractResponse = await _client.GetAsync($"/api/v1/finance/payment-executions/{dispatchedExecution.PaymentGatewayExecutionId}/provider-cancel-contract");
        Assert.Equal(HttpStatusCode.OK, cancelContractResponse.StatusCode);
        var cancelContract = await cancelContractResponse.Content.ReadFromJsonAsync<PaymentGatewayProviderContractResponse>();
        Assert.NotNull(cancelContract);
        Assert.Equal("FAWRY_CANCEL_UNPAID_ORDER", cancelContract!.ContractTypeCode);
        Assert.Contains("/ECommerceWeb/api/orders/cancel-unpaid-order", cancelContract.RequestUrl);
        Assert.Contains("\"orderRefNo\"", cancelContract.RequestBodyJson);
        Assert.Contains(dispatchedExecution.ProviderReference!, cancelContract.RequestBodyJson);
        Assert.Contains("\"signatureRecipe\"", cancelContract.RequestBodyJson);
        Assert.Contains("orderRefNo", cancelContract.RequestBodyJson);
        Assert.Contains("merchantAccount", cancelContract.RequestBodyJson);
        Assert.Contains("secureKey", cancelContract.RequestBodyJson);
    }

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

        var outbound = await CreateAndActivateEndpointAsync("HTTP Paymob refund outbound", "PAYMOB", "OUTBOUND", "/refunds");
        var inbound = await CreateAndActivateEndpointAsync("HTTP Paymob refund callback", "PAYMOB", "INBOUND", "/refund-callback");
        var channel = await CreateAndActivateChannelAsync(outbound, inbound);

        var createReceiptResponse = await _client.PostAsJsonAsync("/api/v1/finance/ar-receipts", new CreateAccountsReceivableReceiptRequest(
            CustomerId: 2,
            ReceiptDate: new DateOnly(2026, 5, 18),
            ReceiptMethod: "BANK_TRANSFER",
            CurrencyCode: "EGP",
            AmountAmount: 140m,
            ReferenceNumber: "HTTP-RFND-ORCH-RCPT-001",
            Notes: "HTTP refund orchestration receipt",
            Allocations: []));
        Assert.Equal(HttpStatusCode.OK, createReceiptResponse.StatusCode);
        var receipt = await createReceiptResponse.Content.ReadFromJsonAsync<AccountsReceivableReceiptResponse>();
        Assert.NotNull(receipt);
        Assert.Equal(HttpStatusCode.OK, (await _client.PostAsync($"/api/v1/finance/ar-receipts/{receipt!.AccountsReceivableReceiptId}/submit", null)).StatusCode);

        await _client.AuthenticateAsFinanceCheckerAsync();
        Assert.Equal(HttpStatusCode.OK, (await _client.PostAsync($"/api/v1/finance/ar-receipts/{receipt.AccountsReceivableReceiptId}/approve", null)).StatusCode);
        Assert.Equal(HttpStatusCode.OK, (await _client.PostAsync($"/api/v1/finance/ar-receipts/{receipt.AccountsReceivableReceiptId}/post", null)).StatusCode);

        await _client.AuthenticateAsFinanceMakerAsync();
        var createRefundResponse = await _client.PostAsJsonAsync("/api/v1/finance/customer-refunds", new CreateCustomerRefundRequest(
            CustomerId: 2,
            SourceAccountsReceivableReceiptId: receipt.AccountsReceivableReceiptId,
            RefundDate: new DateOnly(2026, 5, 19),
            RefundMethod: "PAYMENT_GATEWAY",
            CurrencyCode: "EGP",
            AmountAmount: 90m,
            PaymentChannelConfigurationId: channel.PaymentChannelConfigurationId,
            ReferenceNumber: "HTTP-RFND-ORCH-0001",
            Notes: "HTTP refund orchestration flow"));
        Assert.Equal(HttpStatusCode.OK, createRefundResponse.StatusCode);
        var refund = await createRefundResponse.Content.ReadFromJsonAsync<CustomerRefundResponse>();
        Assert.NotNull(refund);
        Assert.Equal(HttpStatusCode.OK, (await _client.PostAsync($"/api/v1/finance/customer-refunds/{refund!.CustomerRefundId}/submit", null)).StatusCode);

        await _client.AuthenticateAsFinanceCheckerAsync();
        var returnResponse = await _client.PostAsJsonAsync($"/api/v1/finance/customer-refunds/{refund.CustomerRefundId}/return", new ReviewCustomerRefundRequest("Please correct refund evidence."));
        Assert.Equal(HttpStatusCode.OK, returnResponse.StatusCode);
        var returnedRefund = await returnResponse.Content.ReadFromJsonAsync<CustomerRefundResponse>();
        Assert.NotNull(returnedRefund);
        Assert.Equal("RETURNED", returnedRefund!.StatusCode);
        Assert.Equal("Please correct refund evidence.", returnedRefund.ReturnNotes);

        await _client.AuthenticateAsFinanceMakerAsync();
        Assert.Equal(HttpStatusCode.OK, (await _client.PostAsync($"/api/v1/finance/customer-refunds/{refund.CustomerRefundId}/submit", null)).StatusCode);

        await _client.AuthenticateAsFinanceCheckerAsync();
        Assert.Equal(HttpStatusCode.OK, (await _client.PostAsync($"/api/v1/finance/customer-refunds/{refund.CustomerRefundId}/approve", null)).StatusCode);
        Assert.Equal(HttpStatusCode.OK, (await _client.PostAsync($"/api/v1/finance/customer-refunds/{refund.CustomerRefundId}/post", null)).StatusCode);

        await _client.AuthenticateAsFinanceMakerAsync();
        Assert.Equal(HttpStatusCode.OK, (await _client.PostAsync($"/api/v1/finance/customer-refunds/{refund.CustomerRefundId}/queue-provider-execution", null)).StatusCode);

        var orchestrationResponse = await _client.GetAsync($"/api/v1/finance/customer-refunds/{refund.CustomerRefundId}/orchestration");
        Assert.Equal(HttpStatusCode.OK, orchestrationResponse.StatusCode);
        var orchestration = await orchestrationResponse.Content.ReadFromJsonAsync<CustomerRefundOrchestrationResponse>();
        Assert.NotNull(orchestration);
        Assert.Equal(1, orchestration!.TotalExecutionCount);
        Assert.Equal(1, orchestration.QueuedExecutionCount);
        Assert.NotNull(orchestration.ProviderContract);
        Assert.Equal("PAYMOB_REFUND", orchestration.ProviderContract!.ContractTypeCode);
        Assert.NotNull(orchestration.ProviderContract.CallbackUrl);
        Assert.Contains($"/api/v1/payments/channels/{channel.PaymentChannelConfigurationId}/callback", orchestration.ProviderContract.CallbackUrl);
    }

    private async Task<IntegrationEndpointResponse> CreateAndActivateEndpointAsync(string endpointName, string providerCode, string directionCode, string relativePath)
    {
        var endpointSlug = endpointName
            .ToLowerInvariant()
            .Replace(" ", "-")
            .Replace("/", "-");
        var uniqueBaseUrl = $"https://{endpointSlug}-{Guid.NewGuid():N}.gateway.uat.example";
        var createResponse = await _client.PostAsJsonAsync("/api/v1/integrations/endpoints", new CreateIntegrationEndpointRequest(
            EndpointName: endpointName,
            IntegrationProviderCode: providerCode,
            EnvironmentCode: "UAT",
            DirectionCode: directionCode,
            TransportCode: "REST",
            HttpMethodCode: "POST",
            BaseUrl: uniqueBaseUrl,
            RelativePath: relativePath,
            AuthenticationModeCode: "SIGNATURE",
            CredentialReference: $"secret://{providerCode.ToLowerInvariant()}/uat/key",
            TimeoutSeconds: 20,
            RetryPolicyCode: "STANDARD",
            MaxRetryCount: 2,
            IsIdempotencyKeyRequired: true,
            Notes: "Payment provider execution HTTP endpoint"));
        Assert.Equal(HttpStatusCode.OK, createResponse.StatusCode);
        var created = await createResponse.Content.ReadFromJsonAsync<IntegrationEndpointResponse>();
        Assert.NotNull(created);

        Assert.Equal(HttpStatusCode.OK, (await _client.PostAsync($"/api/v1/integrations/endpoints/{created!.IntegrationEndpointId}/submit", null)).StatusCode);

        await _client.AuthenticateAsFinanceCheckerAsync();
        Assert.Equal(HttpStatusCode.OK, (await _client.PostAsync($"/api/v1/integrations/endpoints/{created.IntegrationEndpointId}/approve", null)).StatusCode);
        var activateResponse = await _client.PostAsync($"/api/v1/integrations/endpoints/{created.IntegrationEndpointId}/activate", null);
        Assert.Equal(HttpStatusCode.OK, activateResponse.StatusCode);
        var activated = await activateResponse.Content.ReadFromJsonAsync<IntegrationEndpointResponse>();
        Assert.NotNull(activated);

        await _client.AuthenticateAsFinanceMakerAsync();
        return activated!;
    }

    private async Task<PaymentChannelConfigurationResponse> CreateAndActivateChannelAsync(
        IntegrationEndpointResponse outbound,
        IntegrationEndpointResponse inbound,
        string providerCode = "PAYMOB",
        string usageCode = "CUSTOMER_REFUND",
        string channelName = "HTTP Paymob execution channel",
        string notes = "HTTP execution channel")
    {
        var branchId = Math.Abs(Guid.NewGuid().GetHashCode()) % 100000 + 1000;
        var createResponse = await _client.PostAsJsonAsync("/api/v1/finance/payment-channels", new CreatePaymentChannelConfigurationRequest(
            ChannelName: channelName,
            BranchId: branchId,
            IntegrationProviderCode: providerCode,
            ChannelTypeCode: "CHECKOUT_LINK",
            UsageCode: usageCode,
            CurrencyCode: "EGP",
            OutboundIntegrationEndpointId: outbound.IntegrationEndpointId,
            InboundIntegrationEndpointId: inbound.IntegrationEndpointId,
            SupportsCardTokenReferences: false,
            UsesWalletRuleEngine: false,
            RequiresCustomerReference: true,
            MinimumAmount: 1m,
            MaximumAmount: 5000m,
            SettlementLeadDays: 2,
            Notes: notes));
        Assert.Equal(HttpStatusCode.OK, createResponse.StatusCode);
        var created = await createResponse.Content.ReadFromJsonAsync<PaymentChannelConfigurationResponse>();
        Assert.NotNull(created);

        Assert.Equal(HttpStatusCode.OK, (await _client.PostAsync($"/api/v1/finance/payment-channels/{created!.PaymentChannelConfigurationId}/submit", null)).StatusCode);

        await _client.AuthenticateAsFinanceCheckerAsync();
        Assert.Equal(HttpStatusCode.OK, (await _client.PostAsync($"/api/v1/finance/payment-channels/{created.PaymentChannelConfigurationId}/approve", null)).StatusCode);
        var activateResponse = await _client.PostAsync($"/api/v1/finance/payment-channels/{created.PaymentChannelConfigurationId}/activate", null);
        Assert.Equal(HttpStatusCode.OK, activateResponse.StatusCode);
        var activated = await activateResponse.Content.ReadFromJsonAsync<PaymentChannelConfigurationResponse>();
        Assert.NotNull(activated);

        await _client.AuthenticateAsFinanceMakerAsync();
        return activated!;
    }

    private async Task<AccountsReceivableInvoiceResponse> CreatePostedInvoiceAsync(string referenceNumber, decimal unitPriceAmount)
    {
        await _client.AuthenticateAsFinanceMakerAsync();

        var createInvoiceResponse = await _client.PostAsJsonAsync("/api/v1/finance/ar-invoices", new CreateAccountsReceivableInvoiceRequest(
            CustomerId: 2,
            SalesOrderId: 1,
            DispatchOrderId: 1,
            InvoiceType: "TAX",
            InvoiceDate: new DateOnly(2026, 5, 19),
            TaxDate: new DateOnly(2026, 5, 19),
            DueDate: new DateOnly(2026, 5, 30),
            CurrencyCode: "EGP",
            Lines:
            [
                new CreateAccountsReceivableInvoiceLineRequest(1, 1001, 1m, "EA", unitPriceAmount, "VAT14")
            ]));
        Assert.Equal(HttpStatusCode.OK, createInvoiceResponse.StatusCode);
        var invoice = await createInvoiceResponse.Content.ReadFromJsonAsync<AccountsReceivableInvoiceResponse>();
        Assert.NotNull(invoice);

        Assert.Equal(HttpStatusCode.OK, (await _client.PostAsync($"/api/v1/finance/ar-invoices/{invoice!.AccountsReceivableInvoiceId}/submit", null)).StatusCode);

        await _client.AuthenticateAsFinanceCheckerAsync();
        Assert.Equal(HttpStatusCode.OK, (await _client.PostAsync($"/api/v1/finance/ar-invoices/{invoice.AccountsReceivableInvoiceId}/approve", null)).StatusCode);
        var postInvoiceResponse = await _client.PostAsync($"/api/v1/finance/ar-invoices/{invoice.AccountsReceivableInvoiceId}/post", null);
        Assert.Equal(HttpStatusCode.OK, postInvoiceResponse.StatusCode);
        var postedInvoice = await postInvoiceResponse.Content.ReadFromJsonAsync<AccountsReceivableInvoiceResponse>();
        Assert.NotNull(postedInvoice);

        await _client.AuthenticateAsFinanceMakerAsync();
        return postedInvoice!;
    }

    private async Task<CustomerRefundResponse> CreatePostedRefundAsync(long paymentChannelConfigurationId, string referenceNumber, decimal amount)
    {
        var createReceiptResponse = await _client.PostAsJsonAsync("/api/v1/finance/ar-receipts", new CreateAccountsReceivableReceiptRequest(
            CustomerId: 2,
            ReceiptDate: new DateOnly(2026, 5, 18),
            ReceiptMethod: "BANK_TRANSFER",
            CurrencyCode: "EGP",
            AmountAmount: amount + 10m,
            ReferenceNumber: $"{referenceNumber}-RCPT",
            Notes: "HTTP payment execution receipt",
            Allocations: []));
        Assert.Equal(HttpStatusCode.OK, createReceiptResponse.StatusCode);
        var receipt = await createReceiptResponse.Content.ReadFromJsonAsync<AccountsReceivableReceiptResponse>();
        Assert.NotNull(receipt);

        Assert.Equal(HttpStatusCode.OK, (await _client.PostAsync($"/api/v1/finance/ar-receipts/{receipt!.AccountsReceivableReceiptId}/submit", null)).StatusCode);

        await _client.AuthenticateAsFinanceCheckerAsync();
        Assert.Equal(HttpStatusCode.OK, (await _client.PostAsync($"/api/v1/finance/ar-receipts/{receipt.AccountsReceivableReceiptId}/approve", null)).StatusCode);
        Assert.Equal(HttpStatusCode.OK, (await _client.PostAsync($"/api/v1/finance/ar-receipts/{receipt.AccountsReceivableReceiptId}/post", null)).StatusCode);

        await _client.AuthenticateAsFinanceMakerAsync();

        var createRefundResponse = await _client.PostAsJsonAsync("/api/v1/finance/customer-refunds", new CreateCustomerRefundRequest(
            CustomerId: 2,
            SourceAccountsReceivableReceiptId: receipt.AccountsReceivableReceiptId,
            RefundDate: new DateOnly(2026, 5, 19),
            RefundMethod: "PAYMENT_GATEWAY",
            CurrencyCode: "EGP",
            AmountAmount: amount,
            PaymentChannelConfigurationId: paymentChannelConfigurationId,
            ReferenceNumber: referenceNumber,
            Notes: "HTTP payment execution refund"));
        Assert.Equal(HttpStatusCode.OK, createRefundResponse.StatusCode);
        var refund = await createRefundResponse.Content.ReadFromJsonAsync<CustomerRefundResponse>();
        Assert.NotNull(refund);

        Assert.Equal(HttpStatusCode.OK, (await _client.PostAsync($"/api/v1/finance/customer-refunds/{refund!.CustomerRefundId}/submit", null)).StatusCode);

        await _client.AuthenticateAsFinanceCheckerAsync();
        Assert.Equal(HttpStatusCode.OK, (await _client.PostAsync($"/api/v1/finance/customer-refunds/{refund.CustomerRefundId}/approve", null)).StatusCode);
        var postResponse = await _client.PostAsync($"/api/v1/finance/customer-refunds/{refund.CustomerRefundId}/post", null);
        Assert.Equal(HttpStatusCode.OK, postResponse.StatusCode);
        var postedRefund = await postResponse.Content.ReadFromJsonAsync<CustomerRefundResponse>();
        Assert.NotNull(postedRefund);

        await _client.AuthenticateAsFinanceMakerAsync();
        return postedRefund!;
    }

    private async Task<AccountsPayablePaymentResponse> CreatePostedAccountsPayablePaymentAsync(string referenceNumber, decimal unitPriceAmount)
    {
        await _client.AuthenticateAsFinanceMakerAsync();

        var createBillResponse = await _client.PostAsJsonAsync("/api/v1/procurement/ap-bills", new CreateAccountsPayableBillRequest(
            SupplierId: 1,
            PurchaseOrderId: 1,
            GoodsReceiptId: 1,
            BillDate: new DateOnly(2026, 5, 21),
            DueDate: new DateOnly(2026, 5, 30),
            CurrencyCode: "EGP",
            Lines:
            [
                new CreateAccountsPayableBillLineRequest(1, 1001, 1m, "EA", unitPriceAmount, "VAT14")
            ]));
        Assert.Equal(HttpStatusCode.OK, createBillResponse.StatusCode);
        var bill = await createBillResponse.Content.ReadFromJsonAsync<AccountsPayableBillResponse>();
        Assert.NotNull(bill);

        Assert.Equal(HttpStatusCode.OK, (await _client.PostAsync($"/api/v1/procurement/ap-bills/{bill!.AccountsPayableBillId}/submit", null)).StatusCode);

        await _client.AuthenticateAsFinanceCheckerAsync();
        Assert.Equal(HttpStatusCode.OK, (await _client.PostAsync($"/api/v1/procurement/ap-bills/{bill.AccountsPayableBillId}/approve-match", null)).StatusCode);
        Assert.Equal(HttpStatusCode.OK, (await _client.PostAsync($"/api/v1/procurement/ap-bills/{bill.AccountsPayableBillId}/post", null)).StatusCode);

        await _client.AuthenticateAsFinanceMakerAsync();

        var createPaymentResponse = await _client.PostAsJsonAsync("/api/v1/finance/ap-payments", new CreateAccountsPayablePaymentRequest(
            SupplierId: 1,
            PaymentDate: new DateOnly(2026, 5, 22),
            PaymentMethod: "PAYMENT_GATEWAY",
            CurrencyCode: "EGP",
            AmountAmount: bill.TotalAmount,
            ReferenceNumber: referenceNumber,
            Notes: "Supplier payout QA payment",
            Allocations:
            [
                new CreateAccountsPayablePaymentAllocationRequest(bill.AccountsPayableBillId, bill.TotalAmount, new DateOnly(2026, 5, 22))
            ]));
        Assert.Equal(HttpStatusCode.OK, createPaymentResponse.StatusCode);
        var payment = await createPaymentResponse.Content.ReadFromJsonAsync<AccountsPayablePaymentResponse>();
        Assert.NotNull(payment);

        Assert.Equal(HttpStatusCode.OK, (await _client.PostAsync($"/api/v1/finance/ap-payments/{payment!.AccountsPayablePaymentId}/submit", null)).StatusCode);

        await _client.AuthenticateAsFinanceCheckerAsync();
        Assert.Equal(HttpStatusCode.OK, (await _client.PostAsync($"/api/v1/finance/ap-payments/{payment.AccountsPayablePaymentId}/approve", null)).StatusCode);
        var postedPaymentResponse = await _client.PostAsync($"/api/v1/finance/ap-payments/{payment.AccountsPayablePaymentId}/post", null);
        Assert.Equal(HttpStatusCode.OK, postedPaymentResponse.StatusCode);
        var postedPayment = await postedPaymentResponse.Content.ReadFromJsonAsync<AccountsPayablePaymentResponse>();
        Assert.NotNull(postedPayment);

        await _client.AuthenticateAsFinanceMakerAsync();
        return postedPayment!;
    }
}
