using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using StellaOps.Feedser.Core.Jobs;
using StellaOps.Feedser.Exporter.Json;
using StellaOps.Feedser.Storage.Mongo.Advisories;
using StellaOps.Feedser.Storage.Mongo.Exporting;
using StellaOps.Feedser.Models;

namespace StellaOps.Feedser.Exporter.Json.Tests;

public sealed class JsonExporterDependencyInjectionRoutineTests
{
    [Fact]
    public void Register_AddsJobDefinitionAndServices()
    {
        var services = new ServiceCollection();
        services.AddLogging();
        services.AddSingleton<IAdvisoryStore, StubAdvisoryStore>();
        services.AddSingleton<IExportStateStore, StubExportStateStore>();
        services.AddOptions<JobSchedulerOptions>();

        var configuration = new ConfigurationBuilder()
            .AddInMemoryCollection(new Dictionary<string, string?>())
            .Build();

        var routine = new JsonExporterDependencyInjectionRoutine();
        routine.Register(services, configuration);

        using var provider = services.BuildServiceProvider();
        var optionsAccessor = provider.GetRequiredService<IOptions<JobSchedulerOptions>>();
        var options = optionsAccessor.Value;

        Assert.True(options.Definitions.TryGetValue(JsonExportJob.JobKind, out var definition));
        Assert.Equal(typeof(JsonExportJob), definition.JobType);
        Assert.True(definition.Enabled);

        var exporter = provider.GetRequiredService<JsonFeedExporter>();
        Assert.NotNull(exporter);
    }

    private sealed class StubAdvisoryStore : IAdvisoryStore
    {
        public Task<IReadOnlyList<Advisory>> GetRecentAsync(int limit, CancellationToken cancellationToken)
            => Task.FromResult<IReadOnlyList<Advisory>>(Array.Empty<Advisory>());

        public Task<Advisory?> FindAsync(string advisoryKey, CancellationToken cancellationToken)
            => Task.FromResult<Advisory?>(null);

        public Task UpsertAsync(Advisory advisory, CancellationToken cancellationToken)
            => Task.CompletedTask;

        public IAsyncEnumerable<Advisory> StreamAsync(CancellationToken cancellationToken)
        {
            return Enumerate(cancellationToken);

            static async IAsyncEnumerable<Advisory> Enumerate([EnumeratorCancellation] CancellationToken ct)
            {
                ct.ThrowIfCancellationRequested();
                await Task.Yield();
                yield break;
            }
        }
    }

    private sealed class StubExportStateStore : IExportStateStore
    {
        private ExportStateRecord? _record;

        public Task<ExportStateRecord?> FindAsync(string id, CancellationToken cancellationToken)
            => Task.FromResult(_record);

        public Task<ExportStateRecord> UpsertAsync(ExportStateRecord record, CancellationToken cancellationToken)
        {
            _record = record;
            return Task.FromResult(record);
        }
    }
}
