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

namespace CashLineERP.Api.Tests;

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

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

    [Fact]
    public async Task Payment_Settlement_Batch_Endpoints_Support_Exception_Review_And_Posting()
    {
        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 CreateAndActivateRefundChannelAsync(outbound, inbound);
        var refund = await CreatePostedRefundAsync(channel.PaymentChannelConfigurationId, "HTTP-SETTLE-0001");

        var createResponse = await _client.PostAsJsonAsync("/api/v1/finance/payment-settlement-batches", new CreatePaymentSettlementBatchRequest(
            PaymentChannelConfigurationId: channel.PaymentChannelConfigurationId,
            SettlementDate: new DateOnly(2026, 5, 18),
            ExternalBatchReference: "PAYMOB-HTTP-SET-001",
            Lines:
            [
                new CreatePaymentSettlementBatchLineRequest(
                    CustomerRefundId: refund.CustomerRefundId,
                    SettledAmount: refund.AmountAmount - 5m,
                    FeeAmount: 2m,
                    NetAmount: refund.AmountAmount - 7m,
                    ProviderReference: null,
                    Notes: "HTTP imported variance")
            ]));
        Assert.Equal(HttpStatusCode.OK, createResponse.StatusCode);
        var createdBatch = await createResponse.Content.ReadFromJsonAsync<PaymentSettlementBatchResponse>();
        Assert.NotNull(createdBatch);
        Assert.Equal(2, createdBatch!.OpenExceptionCount);
        Assert.Equal("EGP", createdBatch.BaseCurrencyCode);
        Assert.Equal(1m, createdBatch.FxRateToBase);
        Assert.Equal(createdBatch.TotalInternalAmount, createdBatch.TotalInternalBaseAmount);

        var submitResponse = await _client.PostAsync($"/api/v1/finance/payment-settlement-batches/{createdBatch.PaymentSettlementBatchId}/submit", null);
        Assert.Equal(HttpStatusCode.OK, submitResponse.StatusCode);

        await _client.AuthenticateAsFinanceCheckerAsync();

        var approveResponse = await _client.PostAsync($"/api/v1/finance/payment-settlement-batches/{createdBatch.PaymentSettlementBatchId}/approve", null);
        Assert.Equal(HttpStatusCode.OK, approveResponse.StatusCode);

        var blockedPostResponse = await _client.PostAsync($"/api/v1/finance/payment-settlement-batches/{createdBatch.PaymentSettlementBatchId}/post", null);
        Assert.Equal(HttpStatusCode.BadRequest, blockedPostResponse.StatusCode);

        var queueResponse = await _client.GetAsync($"/api/v1/finance/payment-settlement-exceptions?paymentSettlementBatchId={createdBatch.PaymentSettlementBatchId}&statusCode=OPEN");
        Assert.Equal(HttpStatusCode.OK, queueResponse.StatusCode);
        var openExceptions = await queueResponse.Content.ReadFromJsonAsync<List<PaymentSettlementExceptionResponse>>();
        Assert.NotNull(openExceptions);
        Assert.Equal(2, openExceptions!.Count);

        var missingReferenceExceptionId = openExceptions.Single(entry => entry.ExceptionCode == "MISSING_PROVIDER_REFERENCE").PaymentSettlementExceptionId;
        var amountVarianceExceptionId = openExceptions.Single(entry => entry.ExceptionCode == "AMOUNT_VARIANCE").PaymentSettlementExceptionId;

        var confirmResponse = await _client.PostAsJsonAsync(
            $"/api/v1/finance/payment-settlement-exceptions/{missingReferenceExceptionId}/confirm",
            new ResolvePaymentSettlementExceptionRequest("HTTP provider reference review completed"));
        Assert.Equal(HttpStatusCode.OK, confirmResponse.StatusCode);

        var overrideResponse = await _client.PostAsJsonAsync(
            $"/api/v1/finance/payment-settlement-exceptions/{amountVarianceExceptionId}/override",
            new ResolvePaymentSettlementExceptionRequest("HTTP variance approved by finance"));
        Assert.Equal(HttpStatusCode.OK, overrideResponse.StatusCode);

        var finalPostResponse = await _client.PostAsync($"/api/v1/finance/payment-settlement-batches/{createdBatch.PaymentSettlementBatchId}/post", null);
        Assert.Equal(HttpStatusCode.OK, finalPostResponse.StatusCode);
        var postedBatch = await finalPostResponse.Content.ReadFromJsonAsync<PaymentSettlementBatchResponse>();
        Assert.NotNull(postedBatch);
        Assert.Equal("POSTED", postedBatch!.StatusCode);
        Assert.Equal(0, postedBatch.OpenExceptionCount);
    }

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

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

        await _client.AuthenticateAsFinanceCheckerAsync();

        var approveResponse = await _client.PostAsync($"/api/v1/integrations/endpoints/{created.IntegrationEndpointId}/approve", null);
        Assert.Equal(HttpStatusCode.OK, approveResponse.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> CreateAndActivateRefundChannelAsync(
        IntegrationEndpointResponse outbound,
        IntegrationEndpointResponse inbound)
    {
        var createResponse = await _client.PostAsJsonAsync("/api/v1/finance/payment-channels", new CreatePaymentChannelConfigurationRequest(
            ChannelName: "HTTP refund settlement channel",
            BranchId: 1,
            IntegrationProviderCode: "PAYMOB",
            ChannelTypeCode: "CHECKOUT_LINK",
            UsageCode: "CUSTOMER_REFUND",
            CurrencyCode: "EGP",
            OutboundIntegrationEndpointId: outbound.IntegrationEndpointId,
            InboundIntegrationEndpointId: inbound.IntegrationEndpointId,
            SupportsCardTokenReferences: false,
            UsesWalletRuleEngine: false,
            RequiresCustomerReference: true,
            MinimumAmount: 1m,
            MaximumAmount: 5000m,
            SettlementLeadDays: 2,
            Notes: "HTTP refund settlement channel"));
        Assert.Equal(HttpStatusCode.OK, createResponse.StatusCode);
        var created = await createResponse.Content.ReadFromJsonAsync<PaymentChannelConfigurationResponse>();
        Assert.NotNull(created);

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

        await _client.AuthenticateAsFinanceCheckerAsync();

        var approveResponse = await _client.PostAsync($"/api/v1/finance/payment-channels/{created.PaymentChannelConfigurationId}/approve", null);
        Assert.Equal(HttpStatusCode.OK, approveResponse.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<CustomerRefundResponse> CreatePostedRefundAsync(long paymentChannelConfigurationId, string referenceNumber)
    {
        var receiptCreateResponse = await _client.PostAsJsonAsync("/api/v1/finance/ar-receipts", new CreateAccountsReceivableReceiptRequest(
            CustomerId: 2,
            ReceiptDate: new DateOnly(2026, 5, 18),
            ReceiptMethod: "BANK_TRANSFER",
            CurrencyCode: "EGP",
            AmountAmount: 160m,
            ReferenceNumber: $"{referenceNumber}-RCPT",
            Notes: "HTTP settlement receipt",
            Allocations: []));
        Assert.Equal(HttpStatusCode.OK, receiptCreateResponse.StatusCode);
        var createdReceipt = await receiptCreateResponse.Content.ReadFromJsonAsync<AccountsReceivableReceiptResponse>();
        Assert.NotNull(createdReceipt);

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

        await _client.AuthenticateAsFinanceCheckerAsync();

        var receiptApproveResponse = await _client.PostAsync($"/api/v1/finance/ar-receipts/{createdReceipt.AccountsReceivableReceiptId}/approve", null);
        Assert.Equal(HttpStatusCode.OK, receiptApproveResponse.StatusCode);
        var receiptPostResponse = await _client.PostAsync($"/api/v1/finance/ar-receipts/{createdReceipt.AccountsReceivableReceiptId}/post", null);
        Assert.Equal(HttpStatusCode.OK, receiptPostResponse.StatusCode);
        var postedReceipt = await receiptPostResponse.Content.ReadFromJsonAsync<AccountsReceivableReceiptResponse>();
        Assert.NotNull(postedReceipt);

        await _client.AuthenticateAsFinanceMakerAsync();

        var refundCreateResponse = await _client.PostAsJsonAsync("/api/v1/finance/customer-refunds", new CreateCustomerRefundRequest(
            CustomerId: 2,
            SourceAccountsReceivableReceiptId: postedReceipt!.AccountsReceivableReceiptId,
            RefundDate: new DateOnly(2026, 5, 18),
            RefundMethod: "PAYMENT_GATEWAY",
            CurrencyCode: "EGP",
            AmountAmount: 120m,
            PaymentChannelConfigurationId: paymentChannelConfigurationId,
            ReferenceNumber: referenceNumber,
            Notes: "HTTP settlement refund"));
        Assert.Equal(HttpStatusCode.OK, refundCreateResponse.StatusCode);
        var createdRefund = await refundCreateResponse.Content.ReadFromJsonAsync<CustomerRefundResponse>();
        Assert.NotNull(createdRefund);

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

        await _client.AuthenticateAsFinanceCheckerAsync();

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

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