using System.Diagnostics;
using Autofac;
using LightBDD.MsTest3;
using Microsoft.ApplicationInsights;
using PlanTempus.Components;
using PlanTempus.Components.ModuleRegistry;
using PlanTempus.Components.Outbox;
using PlanTempus.Core.Configurations;
using PlanTempus.Core.Configurations.JsonConfigProvider;
using PlanTempus.Core.Email;
using PlanTempus.Core.ModuleRegistry;
using PlanTempus.Core.Outbox;
using PlanTempus.Core.SeqLogging;
using PlanTempus.Core.Telemetry;
using PlanTempus.Database.ModuleRegistry;
using CrypticWizard.RandomWordGenerator;

namespace PlanTempus.X.BDD;

/// <summary>
/// Base class for BDD tests. Combines LightBDD FeatureFixture with Autofac DI.
/// </summary>
public abstract class BddTestFixture : FeatureFixture
{
    private readonly string _configurationFilePath;
    private OutboxListener _outboxListener;
    private SeqBackgroundService _seqBackgroundService;
    private CancellationTokenSource _cts;

    protected BddTestFixture() : this(null)
    {
    }

    public BddTestFixture(string configurationFilePath)
    {
        if (configurationFilePath is not null)
            _configurationFilePath = configurationFilePath.TrimEnd('/') + "/";
    }

    protected IContainer Container { get; private set; }
    protected ContainerBuilder ContainerBuilder { get; private set; }
    protected ICommandHandler CommandHandler { get; private set; }

    public string GetRandomWord()
    {
        var myWordGenerator = new WordGenerator();
        return myWordGenerator.GetWord(WordGenerator.PartOfSpeech.verb);
    }

    public virtual IConfigurationRoot Configuration()
    {
        var configuration = new ConfigurationBuilder()
            .AddJsonFile($"{_configurationFilePath}appconfiguration.dev.json")
            .Build();

        return configuration;
    }

    [TestInitialize]
    public void SetupContainer()
    {
        var configuration = Configuration();
        var builder = new ContainerBuilder();

        builder.RegisterModule(new DbPostgreSqlModule
        {
            ConnectionString = configuration.GetConnectionString("DefaultConnection")
        });

        builder.RegisterModule(new TelemetryModule
        {
            TelemetryConfig = configuration.GetSection("ApplicationInsights").ToObject<TelemetryConfig>()
        });

        builder.RegisterModule(new SeqLoggingModule
        {
            SeqConfiguration = configuration.GetSection("SeqConfiguration").ToObject<SeqConfiguration>()
        });

        builder.RegisterModule<CommandModule>();
        builder.RegisterModule<SecurityModule>();
        builder.RegisterModule<OutboxModule>();
        builder.RegisterModule(new EmailModule
        {
            PostmarkConfiguration = configuration.GetSection("Postmark").ToObject<PostmarkConfiguration>()
        });

        builder.RegisterType<NotificationChannel>().As<IMessageChannel<string>>().SingleInstance();
        builder.RegisterType<OutboxListener>().SingleInstance();
        builder.RegisterType<SeqBackgroundService>().SingleInstance();

        ContainerBuilder = builder;
        Container = builder.Build();
        CommandHandler = Container.Resolve<ICommandHandler>();

        _cts = new CancellationTokenSource();

        _seqBackgroundService = Container.Resolve<SeqBackgroundService>();
        _seqBackgroundService.StartAsync(_cts.Token);

        _outboxListener = Container.Resolve<OutboxListener>();
        _outboxListener.StartAsync(_cts.Token);

    }

    [TestCleanup]
    public void CleanupContainer()
    {
        // 1. Stop OutboxListener først (så der ikke genereres mere telemetry)
        _cts?.Cancel();
        _outboxListener?.StopAsync(CancellationToken.None).Wait();

        // 2. Stop SeqBackgroundService - den sender stop signal, dræner channel og lukker ned
        _seqBackgroundService?.StopAsync(CancellationToken.None).Wait();

        Trace.Flush();

        if (Container is not null)
        {
            var telemetryClient = Container.Resolve<TelemetryClient>();
            telemetryClient.Flush();

            try
            {
                Container.Dispose();
            }
            catch (System.Threading.Channels.ChannelClosedException)
            {
                // Channel already closed
            }

            Container = null;
        }
    }
}
