using System;
using System.IO;
using System.Linq;
using Microsoft.Extensions.DependencyInjection;
using MongoDB.Bson;
using StellaOps.Feedser.Source.Common.Testing;
using StellaOps.Feedser.Source.Distro.RedHat;
using StellaOps.Feedser.Source.Distro.RedHat.Configuration;
using StellaOps.Feedser.Storage.Mongo;
using StellaOps.Feedser.Storage.Mongo.Advisories;
using StellaOps.Feedser.Testing;
using StellaOps.Feedser.Testing;

namespace StellaOps.Feedser.Source.Distro.RedHat.Tests;

[Collection("mongo-fixture")]
public sealed class RedHatConnectorHarnessTests : IAsyncLifetime
{
    private readonly ConnectorTestHarness _harness;

    public RedHatConnectorHarnessTests(MongoIntegrationFixture fixture)
    {
        _harness = new ConnectorTestHarness(fixture, new DateTimeOffset(2025, 10, 5, 0, 0, 0, TimeSpan.Zero), RedHatOptions.HttpClientName);
    }

    [Fact]
    public async Task FetchParseMap_WithHarness_ProducesCanonicalAdvisory()
    {
        await _harness.ResetAsync();

        var options = new RedHatOptions
        {
            BaseEndpoint = new Uri("https://access.redhat.com/hydra/rest/securitydata"),
            PageSize = 10,
            MaxPagesPerFetch = 2,
            MaxAdvisoriesPerFetch = 5,
            InitialBackfill = TimeSpan.FromDays(1),
            Overlap = TimeSpan.Zero,
            FetchTimeout = TimeSpan.FromSeconds(30),
            UserAgent = "StellaOps.Tests.RedHatHarness/1.0",
        };

        var handler = _harness.Handler;
        var timeProvider = _harness.TimeProvider;

        var summaryUri = new Uri("https://access.redhat.com/hydra/rest/securitydata/csaf.json?after=2025-10-04&per_page=10&page=1");
        var summaryUriPost = new Uri("https://access.redhat.com/hydra/rest/securitydata/csaf.json?after=2025-10-05&per_page=10&page=1");
        var summaryUriPostPage2 = new Uri("https://access.redhat.com/hydra/rest/securitydata/csaf.json?after=2025-10-05&per_page=10&page=2");
        var detailUri = new Uri("https://access.redhat.com/hydra/rest/securitydata/csaf/RHSA-2025:0001.json");
        var detailUri2 = new Uri("https://access.redhat.com/hydra/rest/securitydata/csaf/RHSA-2025:0002.json");

        handler.AddJsonResponse(summaryUri, ReadFixture("summary-page1-repeat.json"));
        handler.AddJsonResponse(summaryUriPost, "[]");
        handler.AddJsonResponse(summaryUriPostPage2, "[]");
        handler.AddJsonResponse(detailUri, ReadFixture("csaf-rhsa-2025-0001.json"));
        handler.AddJsonResponse(detailUri2, ReadFixture("csaf-rhsa-2025-0002.json"));

        await _harness.EnsureServiceProviderAsync(services =>
        {
            services.AddRedHatConnector(opts =>
            {
                opts.BaseEndpoint = options.BaseEndpoint;
                opts.PageSize = options.PageSize;
                opts.MaxPagesPerFetch = options.MaxPagesPerFetch;
                opts.MaxAdvisoriesPerFetch = options.MaxAdvisoriesPerFetch;
                opts.InitialBackfill = options.InitialBackfill;
                opts.Overlap = options.Overlap;
                opts.FetchTimeout = options.FetchTimeout;
                opts.UserAgent = options.UserAgent;
            });
        });

        var provider = _harness.ServiceProvider;

        var stateRepository = provider.GetRequiredService<ISourceStateRepository>();
        await stateRepository.UpsertAsync(
            new SourceStateRecord(
                RedHatConnectorPlugin.SourceName,
                Enabled: true,
                Paused: false,
                Cursor: new BsonDocument(),
                LastSuccess: null,
                LastFailure: null,
                FailCount: 0,
                BackoffUntil: null,
                UpdatedAt: timeProvider.GetUtcNow(),
                LastFailureReason: null),
            CancellationToken.None);

        var connector = new RedHatConnectorPlugin().Create(provider);

        await connector.FetchAsync(provider, CancellationToken.None);
        await connector.ParseAsync(provider, CancellationToken.None);
        await connector.MapAsync(provider, CancellationToken.None);

        var advisoryStore = provider.GetRequiredService<IAdvisoryStore>();
        var advisories = await advisoryStore.GetRecentAsync(5, CancellationToken.None);
        Assert.Equal(2, advisories.Count);
        var advisory = advisories.Single(a => string.Equals(a.AdvisoryKey, "RHSA-2025:0001", StringComparison.Ordinal));
        Assert.Equal("high", advisory.Severity);
        Assert.Contains(advisory.Aliases, alias => alias == "CVE-2025-0001");
        Assert.Empty(advisory.Provenance.Where(p => p.Source == "redhat" && p.Kind == "fetch"));

        var secondAdvisory = advisories.Single(a => string.Equals(a.AdvisoryKey, "RHSA-2025:0002", StringComparison.Ordinal));
        Assert.Equal("medium", secondAdvisory.Severity, ignoreCase: true);
        Assert.Contains(secondAdvisory.Aliases, alias => alias == "CVE-2025-0002");

        var state = await stateRepository.TryGetAsync(RedHatConnectorPlugin.SourceName, CancellationToken.None);
        Assert.NotNull(state);
        Assert.True(state!.Cursor.TryGetValue("pendingDocuments", out var pendingDocs) && pendingDocs.AsBsonArray.Count == 0);
        Assert.True(state.Cursor.TryGetValue("pendingMappings", out var pendingMappings) && pendingMappings.AsBsonArray.Count == 0);
    }

    public Task InitializeAsync() => Task.CompletedTask;

    public Task DisposeAsync() => _harness.ResetAsync();

    private static string ReadFixture(string filename)
    {
        var path = Path.Combine(AppContext.BaseDirectory, "Source", "Distro", "RedHat", "Fixtures", filename);
        return File.ReadAllText(path);
    }
}
