using System.Net;
using System.Net.Http.Json;
using CashLineERP.Application.Integration;
using CashLineERP.Contracts.Integration;
using CashLineERP.Infrastructure.Persistence;
using CashLineERP.Infrastructure.Persistence.Repositories;
using CashLineERP.SharedKernel.IslamicFinance;
using Microsoft.Extensions.DependencyInjection;

namespace CashLineERP.Api.Tests;

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

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

    [Fact]
    public async Task Islamic_Finance_Outbox_Events_Are_Visible_Through_Integration_API()
    {
        await _client.AuthenticateAsInternalAsync();

        IslamicFinanceIntegrationSeedResult seed;
        using (var scope = _factory.Services.CreateScope())
        {
            var dbContext = scope.ServiceProvider.GetRequiredService<CashLineErpDbContext>();
            seed = IslamicFinanceIntegrationTestSeeder.SeedOperationalLifecycle(dbContext);
        }

        var listResponse = await _client.GetAsync("/api/v1/integrations/outbox?statusCode=PENDING&sourceModuleCode=FINANCE");
        Assert.Equal(HttpStatusCode.OK, listResponse.StatusCode);
        var events = await listResponse.Content.ReadFromJsonAsync<OutboxEventResponse[]>();
        Assert.NotNull(events);
        Assert.Contains(events!, item => item.EventType == IslamicFinanceIntegrationEventCatalog.FinancingRequestCreated && item.CorrelationKey == seed.RequestNumber);
        Assert.Contains(events!, item => item.EventType == IslamicFinanceIntegrationEventCatalog.ShariahReviewDecided && item.CorrelationKey == seed.ReviewCaseNumber);
        Assert.Contains(events!, item => item.EventType == IslamicFinanceIntegrationEventCatalog.MurabahaScheduleGenerated && item.CorrelationKey == seed.MurabahaCaseNumber);
        var settlementEvent = Assert.Single(events!, item =>
            item.EventType == IslamicFinanceIntegrationEventCatalog.SettlementExecuted
            && item.CorrelationKey == seed.DisbursementNumber);

        var detailResponse = await _client.GetAsync($"/api/v1/integrations/outbox/{settlementEvent.OutboxEventId}");
        Assert.Equal(HttpStatusCode.OK, detailResponse.StatusCode);

        var publishResponse = await _client.PostAsJsonAsync("/api/v1/integrations/outbox/publish-batch", new PublishOutboxBatchRequest(
            PublisherCode: "IF_HTTP_WORKER",
            BatchSize: 50));
        Assert.Equal(HttpStatusCode.OK, publishResponse.StatusCode);
        var publishResult = await publishResponse.Content.ReadFromJsonAsync<PublishOutboxBatchResultResponse>();
        Assert.NotNull(publishResult);
        Assert.Contains(publishResult!.PublishedEvents, item => item.EventType == IslamicFinanceIntegrationEventCatalog.SettlementExecuted && item.CorrelationKey == seed.DisbursementNumber);
    }

    [Fact]
    public async Task Islamic_Finance_Inbox_Messages_Are_Visible_Through_Integration_API()
    {
        await _client.AuthenticateAsInternalAsync();

        long inboundEndpointId;
        using (var scope = _factory.Services.CreateScope())
        {
            var dbContext = scope.ServiceProvider.GetRequiredService<CashLineErpDbContext>();
            var registryService = new IntegrationEndpointRegistryService(new IntegrationEndpointRegistryRepository(dbContext));

            var endpoint = registryService.CreateIntegrationEndpoint(new CreateIntegrationEndpointRequest(
                EndpointName: "Islamic Finance HTTP inbox endpoint",
                IntegrationProviderCode: "NOTIFICATION",
                EnvironmentCode: "TEST",
                DirectionCode: "INBOUND",
                TransportCode: "WEBHOOK",
                HttpMethodCode: "POST",
                BaseUrl: "https://if-http.test.example",
                RelativePath: "/callbacks/islamic-finance/http",
                AuthenticationModeCode: "SIGNATURE",
                CredentialReference: "secret://if/http/signature",
                TimeoutSeconds: 20,
                RetryPolicyCode: "STANDARD",
                MaxRetryCount: 3,
                IsIdempotencyKeyRequired: true,
                Notes: "Islamic Finance HTTP inbox endpoint"));
            registryService.SubmitIntegrationEndpoint(endpoint.IntegrationEndpointId, 2);
            registryService.ApproveIntegrationEndpoint(endpoint.IntegrationEndpointId, 4);
            inboundEndpointId = registryService.ActivateIntegrationEndpoint(endpoint.IntegrationEndpointId, 4).IntegrationEndpointId;
        }

        var receiveResponse = await _client.PostAsJsonAsync("/api/v1/integrations/inbox/receive", new ReceiveInboxMessageRequest(
            inboundEndpointId,
            IslamicFinanceIntegrationEventCatalog.RequestStatusMessageType,
            "IF-HTTP-REQ-001",
            "REQ-HTTP-001",
            "{\"status\":\"approved\"}"));
        Assert.Equal(HttpStatusCode.OK, receiveResponse.StatusCode);
        var received = await receiveResponse.Content.ReadFromJsonAsync<InboxMessageResponse>();
        Assert.NotNull(received);

        var listResponse = await _client.GetAsync("/api/v1/integrations/inbox?statusCode=RECEIVED&integrationProviderCode=NOTIFICATION");
        Assert.Equal(HttpStatusCode.OK, listResponse.StatusCode);
        var messages = await listResponse.Content.ReadFromJsonAsync<InboxMessageResponse[]>();
        Assert.NotNull(messages);
        Assert.Contains(messages!, item => item.InboxMessageId == received!.InboxMessageId && item.MessageType == IslamicFinanceIntegrationEventCatalog.RequestStatusMessageType);

        var detailResponse = await _client.GetAsync($"/api/v1/integrations/inbox/{received!.InboxMessageId}");
        Assert.Equal(HttpStatusCode.OK, detailResponse.StatusCode);

        var processResponse = await _client.PostAsJsonAsync("/api/v1/integrations/inbox/process-batch", new ProcessInboxBatchRequest(
            ProcessorCode: "IF_HTTP_INBOX_WORKER",
            BatchSize: 10));
        Assert.Equal(HttpStatusCode.OK, processResponse.StatusCode);
        var processResult = await processResponse.Content.ReadFromJsonAsync<ProcessInboxBatchResultResponse>();
        Assert.NotNull(processResult);
        Assert.Contains(processResult!.ProcessedMessages, item => item.InboxMessageId == received.InboxMessageId && item.ProcessedByProcess == "IF_HTTP_INBOX_WORKER");
    }
}
