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

namespace CashLineERP.Api.Tests;

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

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

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

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

        _client.DefaultRequestHeaders.Authorization = null;

        const string payloadText = "{\"transactionStatus\":\"PAID\",\"amount\":125.50}";
        const string externalMessageId = "PAYMOB-CB-HTTP-0001";
        const string correlationKey = "ORDER-HTTP-1001";
        var signature = GatewayCallbackSecurity.ComputeSignature("secret://paymob/uat/key", payloadText, externalMessageId, 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", externalMessageId);
        payload.Headers.Add("X-Correlation-Key", correlationKey);
        payload.Headers.Add("X-Message-Type", "PAYMENT_STATUS");

        var acceptedResponse = await _client.PostAsync($"/api/v1/payments/channels/{channel.PaymentChannelConfigurationId}/callback", payload);
        Assert.Equal(HttpStatusCode.OK, acceptedResponse.StatusCode);
        var accepted = await acceptedResponse.Content.ReadFromJsonAsync<GatewayCallbackReceiveResultResponse>();
        Assert.NotNull(accepted);
        Assert.Equal("ACCEPTED", accepted!.DecisionCode);

        using var duplicatePayload = new StringContent(payloadText, Encoding.UTF8, "application/json");
        duplicatePayload.Headers.Add("X-Callback-Signature", signature);
        duplicatePayload.Headers.Add("X-External-Message-Id", externalMessageId);
        duplicatePayload.Headers.Add("X-Correlation-Key", correlationKey);
        duplicatePayload.Headers.Add("X-Message-Type", "PAYMENT_STATUS");

        var duplicateResponse = await _client.PostAsync($"/api/v1/payments/channels/{channel.PaymentChannelConfigurationId}/callback", duplicatePayload);
        Assert.Equal(HttpStatusCode.Conflict, duplicateResponse.StatusCode);
        var duplicate = await duplicateResponse.Content.ReadFromJsonAsync<GatewayCallbackReceiveResultResponse>();
        Assert.NotNull(duplicate);
        Assert.Equal("REPLAY_BLOCKED", duplicate!.DecisionCode);
    }

    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: "Gateway callback HTTP 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> CreateAndActivateChannelAsync(
        IntegrationEndpointResponse outbound,
        IntegrationEndpointResponse inbound)
    {
        var createResponse = await _client.PostAsJsonAsync("/api/v1/finance/payment-channels", new CreatePaymentChannelConfigurationRequest(
            ChannelName: "HTTP Paymob callback channel",
            BranchId: 1,
            IntegrationProviderCode: "PAYMOB",
            ChannelTypeCode: "CARD_TOKEN",
            UsageCode: "CUSTOMER_COLLECTION",
            CurrencyCode: "EGP",
            OutboundIntegrationEndpointId: outbound.IntegrationEndpointId,
            InboundIntegrationEndpointId: inbound.IntegrationEndpointId,
            SupportsCardTokenReferences: true,
            UsesWalletRuleEngine: false,
            RequiresCustomerReference: true,
            MinimumAmount: 10m,
            MaximumAmount: 5000m,
            SettlementLeadDays: 1,
            Notes: "HTTP callback 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!;
    }
}
