using CashLineERP.Application.Finance;
using CashLineERP.Application.Integration;
using CashLineERP.Contracts.Finance;
using CashLineERP.Contracts.Integration;
using CashLineERP.Infrastructure.Persistence.Repositories;
using CashLineERP.SharedKernel.Finance;

namespace CashLineERP.Api.Tests;

public sealed class GatewayCallbackHardeningServiceQaTests
{
    [Fact]
    public void Gateway_Callback_Accepts_Valid_Signature_And_Blocks_Replay()
    {
        using var dbContext = QaDbContextFactory.CreateContext();

        var integrationService = new IntegrationEndpointRegistryService(new IntegrationEndpointRegistryRepository(dbContext));
        var paymentChannelService = new PaymentChannelConfigurationService(new PaymentChannelConfigurationRepository(dbContext));
        var gatewayCallbackService = new GatewayCallbackService(new GatewayCallbackRepository(dbContext));

        var outbound = CreateAndActivateEndpoint(integrationService, "Paymob outbound", "PAYMOB", "OUTBOUND", "/checkout", 3, 4);
        var inbound = CreateAndActivateEndpoint(integrationService, "Paymob callback", "PAYMOB", "INBOUND", "/callback", 3, 4);
        var channel = CreateAndActivateChannel(paymentChannelService, outbound, inbound, 3, 4);

        const string payload = "{\"transactionStatus\":\"PAID\",\"amount\":125.50}";
        const string externalMessageId = "PAYMOB-CB-0001";
        const string correlationKey = "ORDER-1001";
        var signature = GatewayCallbackSecurity.ComputeSignature("secret://paymob/uat/key", payload, externalMessageId, correlationKey);

        var accepted = gatewayCallbackService.Receive(new GatewayCallbackReceiveRequest(
            channel.PaymentChannelConfigurationId,
            signature,
            externalMessageId,
            correlationKey,
            "PAYMENT_STATUS",
            payload));

        Assert.Equal("ACCEPTED", accepted.DecisionCode);
        Assert.NotNull(accepted.Callback);
        Assert.Equal("RECEIVED", accepted.Callback!.StatusCode);

        var replay = gatewayCallbackService.Receive(new GatewayCallbackReceiveRequest(
            channel.PaymentChannelConfigurationId,
            signature,
            externalMessageId,
            correlationKey,
            "PAYMENT_STATUS",
            payload));

        Assert.Equal("REPLAY_BLOCKED", replay.DecisionCode);
        Assert.NotNull(replay.Callback);
        Assert.Equal(accepted.Callback.InboxMessageId, replay.Callback!.InboxMessageId);
    }

    [Fact]
    public void Gateway_Callback_Rejects_Invalid_Signature()
    {
        using var dbContext = QaDbContextFactory.CreateContext();

        var integrationService = new IntegrationEndpointRegistryService(new IntegrationEndpointRegistryRepository(dbContext));
        var paymentChannelService = new PaymentChannelConfigurationService(new PaymentChannelConfigurationRepository(dbContext));
        var gatewayCallbackService = new GatewayCallbackService(new GatewayCallbackRepository(dbContext));

        var outbound = CreateAndActivateEndpoint(integrationService, "Fawry outbound", "FAWRY", "OUTBOUND", "/checkout", 3, 4);
        var inbound = CreateAndActivateEndpoint(integrationService, "Fawry callback", "FAWRY", "INBOUND", "/callback", 3, 4);
        var channel = CreateAndActivateChannel(paymentChannelService, outbound, inbound, 3, 4, "FAWRY", "CHECKOUT_LINK");

        var result = gatewayCallbackService.Receive(new GatewayCallbackReceiveRequest(
            channel.PaymentChannelConfigurationId,
            "BAD-SIGNATURE",
            "FAWRY-CB-0001",
            "ORDER-2002",
            "PAYMENT_STATUS",
            "{\"transactionStatus\":\"FAILED\"}"));

        Assert.Equal("INVALID_SIGNATURE", result.DecisionCode);
        Assert.Null(result.Callback);
    }

    private static IntegrationEndpointResponse CreateAndActivateEndpoint(
        IntegrationEndpointRegistryService integrationService,
        string endpointName,
        string providerCode,
        string directionCode,
        string relativePath,
        long makerUserId,
        long checkerUserId)
    {
        var created = integrationService.CreateIntegrationEndpoint(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 QA endpoint"));

        integrationService.SubmitIntegrationEndpoint(created.IntegrationEndpointId, makerUserId);
        integrationService.ApproveIntegrationEndpoint(created.IntegrationEndpointId, checkerUserId);
        return integrationService.ActivateIntegrationEndpoint(created.IntegrationEndpointId, checkerUserId);
    }

    private static PaymentChannelConfigurationResponse CreateAndActivateChannel(
        PaymentChannelConfigurationService paymentChannelService,
        IntegrationEndpointResponse outbound,
        IntegrationEndpointResponse inbound,
        long makerUserId,
        long checkerUserId,
        string providerCode = "PAYMOB",
        string channelTypeCode = "CARD_TOKEN")
    {
        var created = paymentChannelService.CreatePaymentChannel(new CreatePaymentChannelConfigurationRequest(
            ChannelName: $"{providerCode} callback channel",
            BranchId: 1,
            IntegrationProviderCode: providerCode,
            ChannelTypeCode: channelTypeCode,
            UsageCode: "CUSTOMER_COLLECTION",
            CurrencyCode: "EGP",
            OutboundIntegrationEndpointId: outbound.IntegrationEndpointId,
            InboundIntegrationEndpointId: inbound.IntegrationEndpointId,
            SupportsCardTokenReferences: channelTypeCode == "CARD_TOKEN",
            UsesWalletRuleEngine: channelTypeCode == "MOBILE_WALLET",
            RequiresCustomerReference: true,
            MinimumAmount: 1m,
            MaximumAmount: 10000m,
            SettlementLeadDays: 1,
            Notes: "Gateway callback QA channel"));

        paymentChannelService.SubmitPaymentChannel(created.PaymentChannelConfigurationId, makerUserId);
        paymentChannelService.ApprovePaymentChannel(created.PaymentChannelConfigurationId, checkerUserId);
        return paymentChannelService.ActivatePaymentChannel(created.PaymentChannelConfigurationId, checkerUserId);
    }
}
